pygeodesy 25.4.8__py2.py3-none-any.whl → 25.4.25__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 (96) hide show
  1. pygeodesy/__init__.py +30 -27
  2. pygeodesy/__main__.py +3 -3
  3. pygeodesy/albers.py +29 -36
  4. pygeodesy/auxilats/_CX_4.py +2 -2
  5. pygeodesy/auxilats/_CX_6.py +2 -2
  6. pygeodesy/auxilats/_CX_8.py +2 -2
  7. pygeodesy/auxilats/_CX_Rs.py +9 -9
  8. pygeodesy/auxilats/__init__.py +3 -3
  9. pygeodesy/auxilats/__main__.py +8 -6
  10. pygeodesy/auxilats/auxAngle.py +2 -2
  11. pygeodesy/auxilats/auxLat.py +5 -5
  12. pygeodesy/auxilats/auxily.py +5 -3
  13. pygeodesy/azimuthal.py +7 -6
  14. pygeodesy/basics.py +31 -17
  15. pygeodesy/booleans.py +12 -10
  16. pygeodesy/cartesianBase.py +21 -20
  17. pygeodesy/clipy.py +11 -10
  18. pygeodesy/constants.py +11 -10
  19. pygeodesy/css.py +14 -11
  20. pygeodesy/datums.py +8 -8
  21. pygeodesy/deprecated/bases.py +2 -2
  22. pygeodesy/deprecated/classes.py +2 -2
  23. pygeodesy/deprecated/consterns.py +4 -4
  24. pygeodesy/dms.py +8 -8
  25. pygeodesy/ecef.py +10 -7
  26. pygeodesy/elevations.py +9 -8
  27. pygeodesy/ellipsoidalBase.py +19 -8
  28. pygeodesy/ellipsoidalBaseDI.py +17 -15
  29. pygeodesy/ellipsoidalNvector.py +6 -3
  30. pygeodesy/ellipsoidalVincenty.py +4 -1
  31. pygeodesy/ellipsoids.py +167 -138
  32. pygeodesy/elliptic.py +9 -9
  33. pygeodesy/errors.py +44 -43
  34. pygeodesy/etm.py +7 -7
  35. pygeodesy/fmath.py +10 -9
  36. pygeodesy/formy.py +11 -12
  37. pygeodesy/frechet.py +216 -109
  38. pygeodesy/fstats.py +5 -4
  39. pygeodesy/fsums.py +78 -77
  40. pygeodesy/gars.py +4 -3
  41. pygeodesy/geodesici.py +15 -14
  42. pygeodesy/geodesicw.py +34 -32
  43. pygeodesy/geodesicx/__init__.py +1 -1
  44. pygeodesy/geodesicx/__main__.py +11 -9
  45. pygeodesy/geodesicx/gx.py +30 -33
  46. pygeodesy/geodesicx/gxarea.py +2 -2
  47. pygeodesy/geodesicx/gxline.py +5 -5
  48. pygeodesy/geodsolve.py +18 -17
  49. pygeodesy/geohash.py +5 -5
  50. pygeodesy/geoids.py +34 -31
  51. pygeodesy/hausdorff.py +17 -13
  52. pygeodesy/heights.py +2 -4
  53. pygeodesy/internals.py +28 -44
  54. pygeodesy/interns.py +10 -7
  55. pygeodesy/iters.py +8 -8
  56. pygeodesy/karney.py +68 -62
  57. pygeodesy/ktm.py +5 -5
  58. pygeodesy/latlonBase.py +14 -18
  59. pygeodesy/lazily.py +65 -63
  60. pygeodesy/lcc.py +11 -9
  61. pygeodesy/ltp.py +8 -7
  62. pygeodesy/ltpTuples.py +2 -2
  63. pygeodesy/mgrs.py +7 -6
  64. pygeodesy/named.py +47 -31
  65. pygeodesy/nvectorBase.py +7 -7
  66. pygeodesy/osgr.py +9 -8
  67. pygeodesy/points.py +12 -10
  68. pygeodesy/props.py +25 -25
  69. pygeodesy/resections.py +11 -10
  70. pygeodesy/rhumb/__init__.py +1 -1
  71. pygeodesy/rhumb/aux_.py +7 -7
  72. pygeodesy/rhumb/bases.py +22 -20
  73. pygeodesy/rhumb/ekx.py +6 -6
  74. pygeodesy/rhumb/solve.py +15 -15
  75. pygeodesy/solveBase.py +3 -3
  76. pygeodesy/sphericalBase.py +6 -6
  77. pygeodesy/sphericalNvector.py +6 -5
  78. pygeodesy/sphericalTrigonometry.py +8 -7
  79. pygeodesy/streprs.py +14 -14
  80. pygeodesy/trf.py +14 -12
  81. pygeodesy/triaxials.py +29 -26
  82. pygeodesy/units.py +5 -4
  83. pygeodesy/unitsBase.py +5 -4
  84. pygeodesy/ups.py +3 -3
  85. pygeodesy/utily.py +4 -4
  86. pygeodesy/utmups.py +4 -4
  87. pygeodesy/utmupsBase.py +88 -18
  88. pygeodesy/vector2d.py +18 -11
  89. pygeodesy/vector3d.py +7 -6
  90. pygeodesy/webmercator.py +6 -5
  91. pygeodesy/wgrs.py +6 -5
  92. {pygeodesy-25.4.8.dist-info → pygeodesy-25.4.25.dist-info}/METADATA +27 -23
  93. pygeodesy-25.4.25.dist-info/RECORD +118 -0
  94. pygeodesy-25.4.8.dist-info/RECORD +0 -118
  95. {pygeodesy-25.4.8.dist-info → pygeodesy-25.4.25.dist-info}/WHEEL +0 -0
  96. {pygeodesy-25.4.8.dist-info → pygeodesy-25.4.25.dist-info}/top_level.txt +0 -0
pygeodesy/trf.py CHANGED
@@ -69,16 +69,18 @@ en/how-to-deal-with-etrs89-datum-and-time-dependent-transformation-parameters-45
69
69
  @var RefFrames.WGS84g1762: RefFrame(name='WGS84g1762', epoch=2005, datum=Datums.GRS80) .Xforms=(0, 0)
70
70
  '''
71
71
 
72
- from pygeodesy.basics import map1, neg, isidentifier, isstr, _xinstanceof, _xscalar
72
+ from pygeodesy.basics import _isin, map1, neg, isidentifier, isstr, _xinstanceof, \
73
+ _xscalar, typename
73
74
  from pygeodesy.constants import _float as _F, _0_0s, _0_0, _0_001, _0_5, _1_0
74
75
  from pygeodesy.datums import Datums, _earth_datum, _equall, _GDA2020_, _Names7, \
75
76
  _negastr, Transform, _WGS84, _EWGS84, _operator
76
77
  # from pygeodesy.ellipsoids import _EWGS84 # from .datums
77
78
  from pygeodesy.errors import TRFError, _xattr, _xellipsoidall, _xkwds, _xkwds_item2
79
+ # from pygeodesy.internals import typename # from .basics
78
80
  from pygeodesy.interns import MISSING, NN, _AT_, _COMMASPACE_, _conversion_, \
79
- _datum_, _DOT_, _exists_, _invalid_, _MINUS_, \
80
- _NAD83_, _no_, _PLUS_, _reframe_, _s_, _SPACE_, \
81
- _STAR_, _to_, _vs_, _WGS84_, _x_, _intern as _i
81
+ _datum_, _DMAIN_, _DOT_, _exists_, _invalid_, _MINUS_, \
82
+ _NAD83_, _no_, _PLUS_, _reframe_, _s_, _SPACE_, _to_, \
83
+ _STAR_, _vs_, _WGS84_, _x_, _intern as _i
82
84
  # from pygeodesy.lazily import _ALL_LAZY # from .units
83
85
  from pygeodesy.named import ADict, classname, _lazyNamedEnumItem as _lazy, _name2__, \
84
86
  _Named, _NamedEnum, _NamedEnumItem, _NamedTuple, Fmt, unstr
@@ -91,7 +93,7 @@ from math import ceil as _ceil, fabs
91
93
  # import operator as _operator # from .datums
92
94
 
93
95
  __all__ = _ALL_LAZY.trf
94
- __version__ = '24.10.14'
96
+ __version__ = '25.04.14'
95
97
 
96
98
  _EP0CH = Epoch(0, low=0)
97
99
  _Es = {_EP0CH: _EP0CH} # L{Epoch}s, deleted below
@@ -176,9 +178,9 @@ class RefFrame(_NamedEnumItem):
176
178
 
177
179
  @raise TypeError: Invalid B{C{datum}}.
178
180
  '''
179
- if datum in (_GRS80, None):
181
+ if _isin(datum, None, _GRS80):
180
182
  pass
181
- elif datum in (_WGS84, _EWGS84):
183
+ elif _isin(datum, _WGS84, _EWGS84):
182
184
  self._datum = _WGS84
183
185
  else:
184
186
  _earth_datum(self, datum, raiser=_datum_)
@@ -1062,8 +1064,8 @@ def _toRefFrame(point, reframe2, reframe=None, epoch=None,
1062
1064
  e2 = e1 if epoch2 is None else Epoch(epoch2=epoch2)
1063
1065
  t0 = _toTransform0(r1.name, e1, reframe2.name, e2)
1064
1066
  if t0 is None:
1065
- t = _SPACE_(RefFrame.__name__, _AT_(r1.name, e1),
1066
- _to_, _AT_(reframe2.name, e2))
1067
+ t = _SPACE_(typename(RefFrame), _AT_(r1.name, e1),
1068
+ _to_, _AT_(reframe2.name, e2))
1067
1069
  raise TRFError(_no_(_conversion_), txt=t)
1068
1070
 
1069
1071
  name, LatLon_kwds = _name2__(name_LatLon_kwds)
@@ -1732,7 +1734,7 @@ trfXform(_WGS84g1150_, _ITRF2000_, epoch=_E(2004), xform=_P_0_0s, rates=_P_0_0s
1732
1734
 
1733
1735
  del _E, _Es, _i, _P, _P_0_0s, _R, _Rs, _X, _Xs
1734
1736
 
1735
- if __name__ == '__main__':
1737
+ if __name__ == _DMAIN_:
1736
1738
 
1737
1739
  def _main():
1738
1740
  from pygeodesy.basics import _args_kwds_names
@@ -1740,8 +1742,8 @@ if __name__ == '__main__':
1740
1742
  from pygeodesy import printf
1741
1743
  from time import localtime
1742
1744
 
1743
- D = date2epoch.__name__
1744
- E = epoch2date.__name__
1745
+ D = typename(date2epoch)
1746
+ E = typename(epoch2date)
1745
1747
  y = localtime()[0]
1746
1748
  for m in range(1, 13):
1747
1749
  for d in (1, 15, _mDays[m] - 1, _mDays[m]):
pygeodesy/triaxials.py CHANGED
@@ -17,20 +17,20 @@ see the U{GeographicLib<https://GeographicLib.SourceForge.io>} documentation.
17
17
  @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)
18
18
  @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)
19
19
  @var Triaxials.Earth: Triaxial(name='Earth', a=6378173.435, b=6378103.9, c=6356754.399999999, e2ab=0.000021804, e2bc=0.006683418, e2ac=0.006705077, volume=1083208241574987694080, area=510065911057441.0625, area_p=510065915922713.6875)
20
- @var Triaxials.Enceladus: Triaxial(name='Enceladus', a=256600, b=251400, c=248300, e2ab=0.040119337, e2bc=0.024509841, e2ac=0.06364586, volume=67094551514082248, area=798618496278.596679688, area_p=798619018175.109863281)
20
+ @var Triaxials.Enceladus: Triaxial(name='Enceladus', a=256600, b=251400, c=248300, e2ab=0.040119337, e2bc=0.024509841, e2ac=0.06364586, volume=67094551514082248, area=798618496278.596679688, area_p=798619018175.109985352)
21
21
  @var Triaxials.Europa: Triaxial(name='Europa', a=1564130, b=1561230, c=1560930, e2ab=0.003704694, e2bc=0.000384275, e2ac=0.004087546, volume=15966575194402123776, area=30663773697323.51953125, area_p=30663773794562.45703125)
22
22
  @var Triaxials.Io: Triaxial(name='Io', a=1829400, b=1819300, c=1815700, e2ab=0.011011391, e2bc=0.003953651, e2ac=0.014921506, volume=25313121117889765376, area=41691875849096.7421875, area_p=41691877397441.2109375)
23
23
  @var Triaxials.Mars: Triaxial(name='Mars', a=3394600, b=3393300, c=3376300, e2ab=0.000765776, e2bc=0.009994646, e2ac=0.010752768, volume=162907283585817247744, area=144249140795107.4375, area_p=144249144150662.15625)
24
- @var Triaxials.Mimas: Triaxial(name='Mimas', a=207400, b=196800, c=190600, e2ab=0.09960581, e2bc=0.062015624, e2ac=0.155444317, volume=32587072869017956, area=493855762247.691894531, area_p=493857714107.9375)
25
- @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.756958008, area_p=698881306767.950317383)
26
- @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.2265625)
24
+ @var Triaxials.Mimas: Triaxial(name='Mimas', a=207400, b=196800, c=190600, e2ab=0.09960581, e2bc=0.062015624, e2ac=0.155444317, volume=32587072869017956, area=493855762247.691833496, area_p=493857714107.9375)
25
+ @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)
26
+ @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)
27
27
  @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)
28
28
  @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)
29
29
  '''
30
30
  # make sure int/int division yields float quotient, see .basics
31
31
  from __future__ import division as _; del _ # PYCHOK semicolon
32
32
 
33
- from pygeodesy.basics import isLatLon, isscalar
33
+ from pygeodesy.basics import _isin, isLatLon, isscalar
34
34
  from pygeodesy.constants import EPS, EPS0, EPS02, EPS4, INT0, PI2, PI_3, PI4, \
35
35
  _EPS2e4, _SQRT2_2, float0_, isfinite, isnear1, _over, \
36
36
  _0_0, _0_5, _1_0, _N_1_0, _64_0, _4_0 # PYCHOK used!
@@ -40,10 +40,10 @@ from pygeodesy.datums import Datum, _spherical_datum, _WGS84, Ellipsoid, _EWGS8
40
40
  from pygeodesy.errors import _AssertionError, _ValueError
41
41
  from pygeodesy.fmath import Fdot, fdot, fmean_, hypot, hypot_, norm2, sqrt0
42
42
  from pygeodesy.fsums import _Fsumf_, fsumf_, fsum1f_
43
- from pygeodesy.interns import NN, _a_, _b_, _beta_, _c_, _distant_, _finite_, \
44
- _height_, _inside_, _near_, _negative_, _not_, \
45
- _NOTEQUAL_, _null_, _opposite_, _outside_, _SPACE_, \
46
- _spherical_, _too_, _x_, _y_
43
+ from pygeodesy.interns import NN, _a_, _b_, _beta_, _c_, _distant_, _DMAIN_, \
44
+ _finite_, _height_, _inside_, _near_, _negative_, \
45
+ _not_, _NOTEQUAL_, _null_, _opposite_, _outside_, \
46
+ _SPACE_, _spherical_, _too_, _x_, _y_
47
47
  # from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS # from .vector3d
48
48
  from pygeodesy.named import _lazyNamedEnumItem as _lazy, _name__, _NamedEnum, \
49
49
  _NamedEnumItem, _Pass
@@ -59,7 +59,7 @@ from pygeodesy.vector3d import _otherV3d, Vector3d, _ALL_LAZY, _MODS
59
59
  from math import fabs, sqrt
60
60
 
61
61
  __all__ = _ALL_LAZY.triaxials
62
- __version__ = '24.11.24'
62
+ __version__ = '25.04.14'
63
63
 
64
64
  _not_ordered_ = _not_('ordered')
65
65
  _omega_ = 'omega'
@@ -258,7 +258,7 @@ class Triaxial_(_NamedEnumItem):
258
258
 
259
259
  @Property_RO
260
260
  def _a2_b2(self):
261
- '''(INTERNAL) Get C{(a/b)**2}.
261
+ '''(INTERNAL) Get C{(a / b)**2}.
262
262
  '''
263
263
  a, b, _ = self._abc3
264
264
  return (a / b)**2 if a != b else _1_0
@@ -321,7 +321,7 @@ class Triaxial_(_NamedEnumItem):
321
321
 
322
322
  @Property_RO
323
323
  def _c2_b2(self):
324
- '''(INTERNAL) Get C{(c/b)**2}.
324
+ '''(INTERNAL) Get C{(c / b)**2}.
325
325
  '''
326
326
  _, b, c = self._abc3
327
327
  return (c / b)**2 if b != c else _1_0
@@ -422,7 +422,7 @@ class Triaxial_(_NamedEnumItem):
422
422
  raise TriaxialError(x=x, y=y, z=z, cause=e)
423
423
  if h > 0 and self.sideOf(v, eps=EPS0) < 0:
424
424
  h = -h # inside
425
- n = _name__(name, name__=self.height4) # _DUNDER_nameof
425
+ n = _name__(name, name__=self.height4) # typename
426
426
  return Vector4Tuple(x, y, z, h, iteration=i, name=n)
427
427
 
428
428
  @Property_RO
@@ -507,7 +507,7 @@ class Triaxial_(_NamedEnumItem):
507
507
  @return: 2-Tuple C{((a, b, c), ijk)} with C{a} >= C{b} >= C{c}
508
508
  and C{ijk} a 3-tuple with the initial indices.
509
509
  '''
510
- i, j, k = 0, 1, 2 # range(3)
510
+ i, j, k = range(3)
511
511
  if a < b:
512
512
  a, b, i, j = b, a, j, i
513
513
  if a < c:
@@ -1245,9 +1245,9 @@ def hartzell4(pov, los=False, tri_biax=_WGS84, **name):
1245
1245
  '''Compute the intersection of a tri-/biaxial ellipsoid and a Line-Of-Sight from
1246
1246
  a Point-Of-View outside.
1247
1247
 
1248
- @arg pov: Point-Of-View outside the tri-/biaxial (C{Cartesian}, L{Ecef9Tuple}
1248
+ @arg pov: Point-Of-View outside the tri-/biaxial (C{Cartesian}, L{Ecef9Tuple},
1249
1249
  C{LatLon} or L{Vector3d}).
1250
- @kwarg los: Line-Of-Sight, I{direction} to the tri-/biaxial (L{Los}, L{Vector3d}),
1250
+ @kwarg los: Line-Of-Sight, I{direction} to the tri-/biaxial (L{Los}, L{Vector3d})
1251
1251
  or C{True} for the I{normal, perpendicular, plumb} to the surface of
1252
1252
  the tri-/biaxial or C{False} or C{None} to point to its center.
1253
1253
  @kwarg tri_biax: A triaxial (L{Triaxial}, L{Triaxial_}, L{JacobiConformal} or
@@ -1275,13 +1275,13 @@ def hartzell4(pov, los=False, tri_biax=_WGS84, **name):
1275
1275
  T = tri_biax
1276
1276
  else:
1277
1277
  D = tri_biax if isinstance(tri_biax, Datum) else \
1278
- _spherical_datum(tri_biax, name__=hartzell4) # _DUNDER_nameof
1278
+ _spherical_datum(tri_biax, name__=hartzell4) # typename
1279
1279
  T = D.ellipsoid._triaxial
1280
1280
  try:
1281
1281
  v, h, i = _hartzell3(pov, los, T)
1282
1282
  except Exception as x:
1283
1283
  raise TriaxialError(pov=pov, los=los, tri_biax=tri_biax, cause=x)
1284
- n = _name__(name, name__=hartzell4) # _DUNDER_nameof
1284
+ n = _name__(name, name__=hartzell4) # typename
1285
1285
  return Vector4Tuple(v.x, v.y, v.z, h, iteration=i, name=n)
1286
1286
 
1287
1287
 
@@ -1478,19 +1478,18 @@ def _rootNd(r, s, u, v, w, g, eps=EPS0):
1478
1478
  '''
1479
1479
  u *= r
1480
1480
  v *= s # 0 for 2-D root
1481
- t0 = w + _N_1_0
1482
- t1 = _0_0 if g < 0 else (hypot_(u, w, v) + _N_1_0)
1481
+ t0 = w - _1_0
1482
+ t1 = _0_0 if g < 0 else (hypot_(u, w, v) - _1_0)
1483
1483
  # assert t0 <= t1
1484
- _e = -eps
1485
- for i in range(1, _TRIPS): # 47..65
1484
+ for i in range(1, _TRIPS): # 48..58
1486
1485
  t = (t1 + t0) * _0_5
1487
1486
  e = t1 - t0
1488
- if _e < e < eps or t in (t0, t1):
1487
+ if eps > e > -eps or _isin(t, t0, t1):
1489
1488
  break
1490
1489
  g = fsumf_(_N_1_0, # ~= _hypot2_1
1491
1490
  _over02(u, t + r),
1492
- _over02(w, t - _N_1_0),
1493
- _over02(v, t + s) if v else _0_0)
1491
+ _over02(w, t + _1_0), (
1492
+ _over02(v, t + s) if v else _0_0))
1494
1493
  if g > 0:
1495
1494
  t0 = t
1496
1495
  elif g < 0:
@@ -1527,7 +1526,7 @@ def _validate(a, b, c, d, T, x, y, z, val):
1527
1526
  dot=e, eps=val)
1528
1527
 
1529
1528
 
1530
- if __name__ == '__main__':
1529
+ if __name__ == _DMAIN_:
1531
1530
 
1532
1531
  from pygeodesy import printf
1533
1532
  from pygeodesy.interns import _COMMA_, _NL_, _NLATvar_
@@ -1540,6 +1539,10 @@ if __name__ == '__main__':
1540
1539
  t = [NN] + Triaxials.toRepr(all=True, asorted=True).split(_NL_)
1541
1540
  printf(_NLATvar_.join(i.strip(_COMMA_) for i in t))
1542
1541
 
1542
+ # % python3 -m pygeodesy.triaxials
1543
+ #
1544
+ # Bektas: height4(x=3909251.554667, y=3909165.750567, z=3170432.501602, h=999.999996)
1545
+
1543
1546
  # **) MIT License
1544
1547
  #
1545
1548
  # Copyright (C) 2022-2025 -- mrJean1 at Gmail -- All Rights Reserved.
pygeodesy/units.py CHANGED
@@ -6,11 +6,12 @@ basic C{float}, C{int} respectively C{str} to named units as L{Degrees},
6
6
  L{Feet}, L{Meter}, L{Radians}, etc.
7
7
  '''
8
8
 
9
- from pygeodesy.basics import isscalar, issubclassof, signOf
9
+ from pygeodesy.basics import isscalar, issubclassof, signOf, typename
10
10
  from pygeodesy.constants import EPS, EPS1, PI, PI2, PI_2, _umod_360, _0_0, \
11
11
  _0_001, _0_5, INT0 # PYCHOK for .mgrs, .namedTuples
12
12
  from pygeodesy.dms import F__F, F__F_, S_NUL, S_SEP, parseDMS, parseRad, _toDMS
13
13
  from pygeodesy.errors import _AssertionError, TRFError, UnitError, _xattr, _xcallable
14
+ # from pygeodesy.internals import typename # from .basics
14
15
  from pygeodesy.interns import NN, _azimuth_, _band_, _bearing_, _COMMASPACE_, \
15
16
  _degrees_, _degrees2_, _distance_, _E_, _easting_, \
16
17
  _epoch_, _EW_, _feet_, _height_, _lam_, _lat_, _LatLon_, \
@@ -27,7 +28,7 @@ from pygeodesy.unitsBase import Float, Int, _NamedUnit, Radius, Str, Fmt, fstr
27
28
  from math import degrees, isnan, radians
28
29
 
29
30
  __all__ = _ALL_LAZY.units
30
- __version__ = '24.11.14'
31
+ __version__ = '25.04.14'
31
32
 
32
33
 
33
34
  class Float_(Float):
@@ -860,8 +861,8 @@ def _std_repr(*Classes):
860
861
  '''
861
862
  from pygeodesy.internals import _getenv
862
863
  for C in Classes:
863
- if hasattr(C, _std_repr.__name__): # PYCHOK del _std_repr
864
- env = 'PYGEODESY_%s_STD_REPR' % (C.__name__.upper(),)
864
+ if hasattr(C, typename(_std_repr)): # PYCHOK del _std_repr
865
+ env = 'PYGEODESY_%s_STD_REPR' % (typename(C).upper(),)
865
866
  if _getenv(env, _std_).lower() != _std_:
866
867
  C._std_repr = False
867
868
 
pygeodesy/unitsBase.py CHANGED
@@ -4,8 +4,9 @@
4
4
  u'''Basic C{Float}, C{Int} and C{Str}ing units classes.
5
5
  '''
6
6
 
7
- from pygeodesy.basics import isstr, issubclassof, _xsubclassof
7
+ from pygeodesy.basics import _isin, isstr, issubclassof, _xsubclassof, typename
8
8
  from pygeodesy.errors import _IsnotError, _UnexpectedError, UnitError, _XError
9
+ # from pygeodesy.internals import typename # from .basics
9
10
  from pygeodesy.interns import NN, _degrees_, _degrees2_, _invalid_, _meter_, \
10
11
  _radians_, _radians2_, _radius_, _UNDER_, _units_, \
11
12
  _std_ # PYCHOK used!
@@ -15,7 +16,7 @@ from pygeodesy.named import modulename, _Named, property_doc_
15
16
  from pygeodesy.streprs import Fmt, fstr
16
17
 
17
18
  __all__ = _ALL_LAZY.unitsBase
18
- __version__ = '24.08.13'
19
+ __version__ = '25.04.14'
19
20
 
20
21
 
21
22
  class _NamedUnit(_Named):
@@ -49,7 +50,7 @@ class _NamedUnit(_Named):
49
50
  elif name:
50
51
  pass
51
52
  elif name__ is not None:
52
- name = name__.__name__
53
+ name = typename(name__)
53
54
  return name, arg
54
55
 
55
56
  @staticmethod # PYCHOK unused suffix
@@ -84,7 +85,7 @@ class _NamedUnit(_Named):
84
85
  def std_repr(self, std):
85
86
  '''Set the representation (C{True} or C{"std"} for standard).
86
87
  '''
87
- self._std_repr = std in (True, _std_)
88
+ self._std_repr = _isin(std, True, _std_)
88
89
 
89
90
  def _toRepr(self, value):
90
91
  '''(INTERNAL) Representation "<name> (<value>)" or "<classname>(<value>)".
pygeodesy/ups.py CHANGED
@@ -28,7 +28,7 @@ from pygeodesy.datums import _ellipsoidal_datum, _WGS84
28
28
  from pygeodesy.dms import degDMS, _neg, parseDMS2
29
29
  from pygeodesy.errors import RangeError, _ValueError, _xkwds_pop2
30
30
  from pygeodesy.fmath import hypot, hypot1, sqrt0
31
- from pygeodesy.internals import _getPYGEODESY, _under
31
+ from pygeodesy.internals import _envPYGEODESY, _under
32
32
  from pygeodesy.interns import NN, _COMMASPACE_, _inside_, _N_, \
33
33
  _pole_, _range_, _S_, _scale0_, \
34
34
  _SPACE_, _std_, _to_, _UTM_
@@ -49,9 +49,9 @@ from pygeodesy.utmupsBase import Fmt, _LLEB, _hemi, _parseUTMUPS5, _to4lldn, \
49
49
  from math import fabs, radians, tan # as _tan
50
50
 
51
51
  __all__ = _ALL_LAZY.ups
52
- __version__ = '24.11.26'
52
+ __version__ = '25.04.14'
53
53
 
54
- _BZ_UPS = _getPYGEODESY('UPS_POLES', _std_) == _std_
54
+ _BZ_UPS = _envPYGEODESY('UPS_POLES', _std_) == _std_
55
55
  _Falsing = Meter(2000e3) # false easting and northing (C{meter})
56
56
  _K0_UPS = Float(_K0_UPS= 0.994) # scale factor at central meridian
57
57
  _K1_UPS = Float(_K1_UPS=_1_0) # rescale point scale factor
pygeodesy/utily.py CHANGED
@@ -18,7 +18,7 @@ from pygeodesy.constants import EPS, EPS0, INF, NAN, PI, PI2, PI_2, R_M, \
18
18
  _copysign_0_0, _float, _isfinite, isnan, isnear0, \
19
19
  _over, _umod_360, _umod_PI2
20
20
  from pygeodesy.errors import _ValueError, _xkwds, _ALL_LAZY, _MODS
21
- from pygeodesy.internals import _passargs
21
+ from pygeodesy.internals import _passargs, typename
22
22
  from pygeodesy.interns import _edge_, _radians_, _semi_circular_, _SPACE_
23
23
  # from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS # from .errors
24
24
  from pygeodesy.units import Degrees, Degrees_, Feet, Float, Lam, Lamd, \
@@ -28,7 +28,7 @@ from math import acos, asin, atan2 as _atan2, cos, degrees, fabs, radians, \
28
28
  sin, tan as _tan # pow
29
29
 
30
30
  __all__ = _ALL_LAZY.utily
31
- __version__ = '25.01.05'
31
+ __version__ = '25.04.14'
32
32
 
33
33
  _G_DEG = _float( 400.0 / _360_0) # grades per degree
34
34
  _G_RAD = _float( 400.0 / PI2) # grades per radian
@@ -1109,7 +1109,7 @@ def unrollPI(rad1, rad2, wrap=True):
1109
1109
  def _valueError(where, x, raiser=True, **kwds):
1110
1110
  '''(INTERNAL) Return a C{_ValueError} or C{None}.
1111
1111
  '''
1112
- t = _MODS.streprs.Fmt.PAREN(where.__name__, x)
1112
+ t = _MODS.streprs.Fmt.PAREN(typename(where), x)
1113
1113
  return _ValueError(t, **kwds) if raiser else None
1114
1114
 
1115
1115
 
@@ -1193,7 +1193,7 @@ class _Wrap(object):
1193
1193
  lat, lon = ll.latlon
1194
1194
  if fabs(lon) > _180_0 or fabs(lat) > _90_0:
1195
1195
  _n = self.latlon
1196
- ll = ll.copy(name=_n.__name__)
1196
+ ll = ll.copy(name=typename(_n))
1197
1197
  ll.latlon = _n(lat, lon)
1198
1198
  return ll
1199
1199
 
pygeodesy/utmups.py CHANGED
@@ -14,14 +14,14 @@ A pure Python implementation, partially transcoded from C++ class U{UTMUPS
14
14
  <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1UTMUPS.html>}
15
15
  by I{Charles Karney}.
16
16
  '''
17
- # from pygeodesy.basics import map1 # from .namedTuples
17
+ from pygeodesy.basics import _isin, map1
18
18
  # from pygeodesy.datums import _WGS84 # from .utmupsBase
19
19
  from pygeodesy.errors import _IsnotError, RangeError, _ValueError, _xkwds_pop2
20
20
  from pygeodesy.interns import NN, _easting_, _MGRS_, _northing_, _NS_, \
21
21
  _outside_, _range_, _SPACE_, _UPS_, _UTM_
22
22
  from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS
23
23
  from pygeodesy.named import modulename
24
- from pygeodesy.namedTuples import UtmUps5Tuple, UtmUps8Tuple, map1
24
+ from pygeodesy.namedTuples import UtmUps5Tuple, UtmUps8Tuple
25
25
  # from pygeodesy.streprs import Fmt # from .utmupsBase
26
26
  from pygeodesy.units import Northing, _100km
27
27
  from pygeodesy.ups import parseUPS5, toUps8, Ups, UPSError, upsZoneBand5
@@ -31,7 +31,7 @@ from pygeodesy.utmupsBase import Fmt, _to4lldn, _to3zBhp, _UPS_ZONE, \
31
31
  _UTMUPS_ZONE_MAX, _WGS84
32
32
 
33
33
  __all__ = _ALL_LAZY.utmups
34
- __version__ = '24.06.11'
34
+ __version__ = '25.04.14'
35
35
 
36
36
  _MGRS_TILE = _100km # in .mgrs.Mgrs.tile
37
37
 
@@ -179,7 +179,7 @@ def UtmUps(zone, hemipole, easting, northing, band=NN, datum=_WGS84, falsed=True
179
179
  <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1UTMUPS.html>}.
180
180
  '''
181
181
  z, B, hp = _to3zBhp(zone, band, hemipole=hemipole)
182
- U = Ups if z in (_UPS_ZONE, _UPS_ZONE_STR) else Utm
182
+ U = Ups if _isin(z, _UPS_ZONE, _UPS_ZONE_STR) else Utm
183
183
  return U(z, hp, easting, northing, band=B, datum=datum, falsed=falsed, **name)
184
184
 
185
185
 
pygeodesy/utmupsBase.py CHANGED
@@ -4,39 +4,44 @@
4
4
  u'''(INTERNAL) Private class C{UtmUpsBase}, functions and constants
5
5
  for modules L{epsg}, L{etm}, L{mgrs}, L{ups} and L{utm}.
6
6
  '''
7
+ # make sure int/int division yields float quotient, see .basics
8
+ from __future__ import division as _; del _ # PYCHOK semicolon
7
9
 
8
- from pygeodesy.basics import isint, isscalar, isstr, neg_, \
9
- _xinstanceof, _xsubclassof
10
+ from pygeodesy.basics import _isin, isint, isscalar, isstr, neg_, \
11
+ _xinstanceof, _xsubclassof
10
12
  from pygeodesy.constants import _float, _0_0, _0_5, _N_90_0, _180_0
11
13
  from pygeodesy.datums import _ellipsoidal_datum, _WGS84
12
14
  from pygeodesy.dms import degDMS, parseDMS2
13
15
  from pygeodesy.ellipsoidalBase import LatLonEllipsoidalBase as _LLEB
14
16
  from pygeodesy.errors import _or, ParseError, _parseX, _ValueError, \
15
17
  _xattrs, _xkwds, _xkwds_not
18
+ # from pygeodesy.fsums import Fsum # _MODS
16
19
  # from pygeodesy.internals import _name__, _under # from .named
17
- from pygeodesy.interns import NN, _A_, _B_, _COMMA_, _Error_, \
18
- _gamma_, _n_a_, _not_, _N_, _NS_, _PLUS_, \
19
- _S_, _scale_, _SPACE_, _Y_, _Z_
20
+ from pygeodesy.interns import NN, _A_, _B_, _COMMA_, _Error_, _gamma_, \
21
+ _n_a_, _not_, _N_, _NS_, _PLUS_, _scale_, \
22
+ _S_, _SPACE_, _Y_, _Z_
20
23
  from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS
21
24
  from pygeodesy.named import _name__, _NamedBase, _under
22
25
  from pygeodesy.namedTuples import EasNor2Tuple, LatLonDatum5Tuple
23
26
  from pygeodesy.props import deprecated_method, property_doc_, _update_all, \
24
27
  deprecated_property_RO, Property_RO, property_RO
25
28
  from pygeodesy.streprs import Fmt, fstr, _fstrENH2, _xzipairs
26
- from pygeodesy.units import Band, Easting, Northing, Scalar, Zone
27
- from pygeodesy.utily import _Wrap, wrap360
29
+ from pygeodesy.units import Band, Easting, Lat, Northing, Phi, Scalar, Zone
30
+ from pygeodesy.utily import atan1, _Wrap, wrap360
31
+
32
+ from math import cos, degrees, fabs, sin, tan
28
33
 
29
34
  __all__ = _ALL_LAZY.utmupsBase
30
- __version__ = '24.08.13'
35
+ __version__ = '25.04.25'
31
36
 
32
37
  _UPS_BANDS = _A_, _B_, _Y_, _Z_ # UPS polar bands SE, SW, NE, NW
33
38
  # _UTM_BANDS = _MODS.utm._Bands
34
39
 
35
- _UTM_LAT_MAX = _float( 84) # PYCHOK for export (C{degrees})
36
- _UTM_LAT_MIN = _float(-80) # PYCHOK for export (C{degrees})
40
+ _UTM_LAT_MAX = _float( 84) # PYCHOK for export (C{degrees})
41
+ _UTM_LAT_MIN = _float(-80) # PYCHOK for export (C{degrees})
37
42
 
38
- _UPS_LAT_MAX = _UTM_LAT_MAX - _0_5 # PYCHOK includes 30' UTM overlap
39
- _UPS_LAT_MIN = _UTM_LAT_MIN + _0_5 # PYCHOK includes 30' UTM overlap
43
+ _UPS_LAT_MAX = _UTM_LAT_MAX - _0_5 # PYCHOK includes 30' UTM overlap
44
+ _UPS_LAT_MIN = _UTM_LAT_MIN + _0_5 # PYCHOK includes 30' UTM overlap
40
45
 
41
46
  _UPS_LATS = {_A_: _N_90_0, _Y_: _UTM_LAT_MAX, # UPS band bottom latitudes,
42
47
  _B_: _N_90_0, _Z_: _UTM_LAT_MAX} # PYCHOK see .Mgrs.bandLatitude
@@ -91,7 +96,7 @@ class UtmUpsBase(_NamedBase):
91
96
  if band:
92
97
  self._band1(band)
93
98
 
94
- if datum not in (None, self._datum):
99
+ if not _isin(datum, None, self._datum):
95
100
  self._datum = _ellipsoidal_datum(datum) # raiser=_datum_, name=band
96
101
 
97
102
  if not falsed:
@@ -189,6 +194,45 @@ class UtmUpsBase(_NamedBase):
189
194
  '''I{Must be overloaded}.'''
190
195
  self._notOverloaded(self)
191
196
 
197
+ def _footpoint(self, y, lat0, makris):
198
+ '''(INTERNAL) Compute to foot-point latitude in C{radians}.
199
+ '''
200
+ if y is None:
201
+ _, y = self.eastingnorthing2(falsed=False)
202
+ S = _MODS.fsums.Fsum
203
+ E = self.datum.ellipsoid
204
+ P = S(E.Llat(lat0), y)
205
+ if E.isSpherical:
206
+ p = P.fover(E.a)
207
+ elif makris:
208
+ r = P.fover(E.b)
209
+ p = fabs(r)
210
+ if p:
211
+ e2 = E.e22 # E.e22abs?
212
+ e4 = E.e4
213
+
214
+ e1 = S(e2 / 4, -11 / 64 * e4, -1).as_iscalar
215
+ e2 = S(e2 / 8, -13 / 128 * e4) .as_iscalar
216
+ e4 *= cos(p)**2 / 8
217
+
218
+ s = sin(p * 2)
219
+ p = -p
220
+ U = S(e1 * p, e2 * s, e4 * p, (5 / 8 * e4) * s**2)
221
+ p = atan1(E.a * tan(float(U)) / E.b)
222
+ if r < 0: # copysign(p, y)
223
+ p = -p
224
+ else: # PyGeodetics
225
+ f = E.f
226
+ f2 = f**2
227
+ f3 = f**3
228
+ B0 = S(1, -f / 2, f2 / 16, f3 / 32) * E.a
229
+ r = P.fover(B0)
230
+ P = S(r, S(3 / 4 * f, 3 / 8 * f2, 21 / 256 * f3) * sin(r * 2),
231
+ S( 21 / 64 * f2, 21 / 64 * f3) * sin(r * 4),
232
+ 151 / 768 * f3 * sin(r * 6))
233
+ p = float(P)
234
+ return p
235
+
192
236
  @Property_RO
193
237
  def gamma(self):
194
238
  '''Get the meridian convergence (C{degrees}) or C{None}
@@ -204,6 +248,23 @@ class UtmUpsBase(_NamedBase):
204
248
  self._toLLEB()
205
249
  return self._hemisphere
206
250
 
251
+ def latFootPoint(self, northing=None, lat0=0, makris=False):
252
+ '''Compute the foot-point latitude in C{degrees}.
253
+
254
+ @arg northing: Northing (C{meter}, same units this datum's ellipsoid's axes),
255
+ overriding this I{unfalsed} northing.
256
+ @kwarg lat0: Geodetic latitude of the meridian's origin (C{degrees}).
257
+ @kwarg makris: If C{True}, use C{Makris}' formula, otherwise C{PyGeodetics}'.
258
+
259
+ @return: Foot-point latitude (C{degrees}).
260
+
261
+ @see: U{PyGeodetics<https://GitHub.com/paarnes/pygeodetics>}, U{FootpointLatitude
262
+ <https://GitHub.com/APRIL-ZJU/clins/blob/master/include/utils/gps_convert_utils.h#L143>},
263
+ U{Makris<https://www.TandFonline.com/doi/abs/10.1179/sre.1982.26.205.345>} and
264
+ U{Geomatics' Mercator, page 60<https://Geomatics.CC/legacy-files/mercator.pdf>}.
265
+ '''
266
+ return Lat(FootPoint=degrees(self._footpoint(northing, lat0, makris)))
267
+
207
268
  def _latlon5(self, LatLon, **LatLon_kwds):
208
269
  '''(INTERNAL) Get cached C{._toLLEB} as B{C{LatLon}} instance.
209
270
  '''
@@ -255,6 +316,15 @@ class UtmUpsBase(_NamedBase):
255
316
  '''
256
317
  return self._northing
257
318
 
319
+ def phiFootPoint(self, northing=None, lat0=0, makris=False):
320
+ '''Compute the foot-point latitude in C{radians}.
321
+
322
+ @return: Foot-point latitude (C{radians}).
323
+
324
+ @see: Method L{latFootPoint<UtmUpsBase.latFootPoint>} for further details.
325
+ '''
326
+ return Phi(FootPoint=self._footpoint(northing, lat0, makris))
327
+
258
328
  @Property_RO
259
329
  def scale(self):
260
330
  '''Get the grid scale (C{float}) or C{None}.
@@ -302,9 +372,9 @@ class UtmUpsBase(_NamedBase):
302
372
  @note: If not specified, the I{latitudinal} C{band} is computed from
303
373
  the (geodetic) latitude and the C{datum}.
304
374
  '''
305
- return self._mgrs if center in (False, 0, _0_0) else (
306
- self._mgrs_lowerleft if center in (True,) else
307
- _toMgrs(_lowerleft(self, center))) # PYCHOK indent
375
+ return self._mgrs_lowerleft if center is True else (
376
+ _toMgrs(_lowerleft(self, center)) if center else
377
+ self._mgrs) # PYCHOK indent
308
378
 
309
379
  def _toRepr(self, fmt, B, cs, prec, sep): # PYCHOK expected
310
380
  '''(INTERNAL) Return a representation for this ETM/UTM/UPS coordinate.
@@ -349,8 +419,8 @@ def _lowerleft(utmups, center): # in .ellipsoidalBase._lowerleft
349
419
  t = c * 2
350
420
  e = int(utmups.easting % t)
351
421
  n = int(utmups.northing % t)
352
- if (e == c and n in (c, c - 1)) or \
353
- (n == c and e in (c, c - 1)):
422
+ if (e == c and _isin(n, c, c - 1)) or \
423
+ (n == c and _isin(e, c, c - 1)):
354
424
  break
355
425
  else:
356
426
  return utmups # unchanged