pygeodesy 24.7.24__py2.py3-none-any.whl → 24.8.4__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/geoids.py CHANGED
@@ -117,7 +117,7 @@ except ImportError: # Python 3+
117
117
  from io import BytesIO as _BytesIO # PYCHOK expected
118
118
 
119
119
  __all__ = _ALL_LAZY.geoids
120
- __version__ = '24.07.12'
120
+ __version__ = '24.07.25'
121
121
 
122
122
  _assert_ = 'assert'
123
123
  _bHASH_ = b'#'
@@ -287,7 +287,7 @@ class _GeoidBase(_HeightsBase):
287
287
 
288
288
  def _called(self, llis, scipy, wrap=False, H=False):
289
289
  # handle __call__
290
- _H = _MODS.formy.heightOrthometric if H else None
290
+ _H = self._heightOrthometric if H else None
291
291
  _as, llis = _as_llis2(llis, Error=GeoidError)
292
292
  hs, _w = [], _Wrap._latlonop(wrap)
293
293
  _a, _h = hs.append, self._hGeoid
@@ -416,6 +416,10 @@ class _GeoidBase(_HeightsBase):
416
416
  '''
417
417
  return _height_called(self, lats, lons, Error=GeoidError, **wrap)
418
418
 
419
+ @property_ROver
420
+ def _heightOrthometric(self):
421
+ return _MODS.formy.heightOrthometric # overwrite property_ROver
422
+
419
423
  def _hGeoid(self, lat, lon):
420
424
  out = self.outside(lat, lon)
421
425
  if out:
pygeodesy/heights.py CHANGED
@@ -69,7 +69,7 @@ C{>>> hs = hinterpolator.height(lats, lons)}
69
69
  from __future__ import division as _; del _ # PYCHOK semicolon
70
70
 
71
71
  from pygeodesy.basics import isscalar, len2, map1, map2, _xnumpy, _xscipy
72
- from pygeodesy.constants import EPS, PI, PI2, _0_0, _90_0, _180_0
72
+ from pygeodesy.constants import EPS, PI, PI_2, PI2, _0_0, _90_0, _180_0
73
73
  from pygeodesy.datums import _ellipsoidal_datum, _WGS84
74
74
  from pygeodesy.errors import _AssertionError, LenError, PointsError, \
75
75
  _SciPyIssue, _xattr, _xkwds, _xkwds_get, _xkwds_item2
@@ -83,7 +83,7 @@ from pygeodesy.interns import NN, _COMMASPACE_, _cubic_, _insufficient_, _linear
83
83
  from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS, _FOR_DOCS
84
84
  from pygeodesy.named import _name2__, _Named
85
85
  from pygeodesy.points import _distanceTo, LatLon_, Fmt, radians, _Wrap
86
- from pygeodesy.props import Property_RO, property_RO
86
+ from pygeodesy.props import Property_RO, property_RO, property_ROver
87
87
  # from pygeodesy.streprs import Fmt # from .points
88
88
  from pygeodesy.units import _isDegrees, Float_, Int_
89
89
  # from pygeodesy.utily import _Wrap # from .points
@@ -91,7 +91,7 @@ from pygeodesy.units import _isDegrees, Float_, Int_
91
91
  # from math import radians # from .points
92
92
 
93
93
  __all__ = _ALL_LAZY.heights
94
- __version__ = '24.06.22'
94
+ __version__ = '24.07.25'
95
95
 
96
96
  _error_ = 'error'
97
97
  _formy = _MODS.into(formy=__name__)
@@ -169,14 +169,13 @@ def _insufficientError(need, Error=HeightError, **name_value): # PYCHOK no cove
169
169
  return Error(txt=t, **name_value)
170
170
 
171
171
 
172
- def _ordedup(ts, lo=EPS, hi=PI2-EPS):
172
+ def _orderedup(ts, lo=EPS, hi=PI2-EPS):
173
173
  # clip, order and remove duplicates
174
- # p, ks = 0, []
174
+ # p = 0
175
175
  # for k in sorted(max(lo, min(hi, t)) for t in ts):
176
176
  # if k > p:
177
- # ks.append(k)
177
+ # yield k
178
178
  # p = k
179
- # return ks
180
179
  return sorted(set(max(lo, min(hi, t)) for t in ts)) # list
181
180
 
182
181
 
@@ -227,8 +226,6 @@ class _HeightBase(_Named): # in .geoids
227
226
  class _HeightsBase(_HeightBase): # in .geoids
228
227
  '''(INTERNAL) Interpolator base class.
229
228
  '''
230
- _np_sp = None # (numpy, scipy)
231
-
232
229
  def __call__(self, *llis, **wrap): # PYCHOK no cover
233
230
  '''Interpolate the height for one or several locations. I{Must be overloaded}.
234
231
 
@@ -290,46 +287,37 @@ class _HeightsBase(_HeightBase): # in .geoids
290
287
  '''
291
288
  self._notOverloaded(lats, lons, **wrap)
292
289
 
293
- def _np_sp2(self, throwarnings=False):
290
+ def _np_sp2(self, throwarnings=False): # PYCHOK no cover
294
291
  '''(INTERNAL) Import C{numpy} and C{scipy}, once.
295
292
  '''
296
- t = _HeightsBase._np_sp
297
- if not t:
298
- # raise SciPyWarnings, but not if
299
- # scipy has already been imported
300
- if throwarnings: # PYCHOK no cover
301
- import sys
302
- if _scipy_ not in sys.modules:
303
- import warnings
304
- warnings.filterwarnings(_error_)
305
-
306
- sp = _xscipy(self.__class__, 1, 2)
307
- np = _xnumpy(self.__class__, 1, 9)
308
-
309
- _HeightsBase._np_sp = t = np, sp
310
- return t
311
-
312
- @Property_RO
293
+ # raise SciPyWarnings, but not if
294
+ # scipy has already been imported
295
+ if throwarnings: # PYCHOK no cover
296
+ import sys
297
+ if _scipy_ not in sys.modules:
298
+ import warnings
299
+ warnings.filterwarnings(_error_)
300
+ return self.numpy, self.scipy
301
+
302
+ @property_ROver
313
303
  def numpy(self):
314
304
  '''Get the C{numpy} module or C{None}.
315
305
  '''
316
- np, _ = self._np_sp2()
317
- return np
306
+ return _xnumpy(self.__class__, 1, 9) # overwrite property_ROver
318
307
 
319
- @Property_RO
308
+ @property_ROver
320
309
  def scipy(self):
321
310
  '''Get the C{scipy} module or C{None}.
322
311
  '''
323
- _, sp = self._np_sp2()
324
- return sp
312
+ return _xscipy(self.__class__, 1, 2) # overwrite property_ROver
325
313
 
326
- @Property_RO
314
+ @property_ROver
327
315
  def scipy_interpolate(self):
328
316
  '''Get the C{scipy.interpolate} module or C{None}.
329
317
  '''
330
318
  _ = self.scipy
331
319
  import scipy.interpolate as spi # scipy 1.2.2
332
- return spi
320
+ return spi # overwrite property_ROver
333
321
 
334
322
  def _scipy_version(self, **n):
335
323
  '''Get the C{scipy} version as 2- or 3-tuple C{(major, minor, micro)}.
@@ -441,19 +429,20 @@ class HeightLSQBiSpline(_HeightsBase):
441
429
  '''
442
430
  _kmin = 16 # k = 3, always
443
431
 
444
- def __init__(self, knots, weight=None, **name_wrap):
432
+ def __init__(self, knots, weight=None, low=1e-4, **name_wrap):
445
433
  '''New L{HeightLSQBiSpline} interpolator.
446
434
 
447
435
  @arg knots: The points with known height (C{LatLon}s).
448
436
  @kwarg weight: Optional weight or weights for each B{C{knot}}
449
437
  (C{scalar} or C{scalar}s).
438
+ @kwarg low: Optional lower bound for I{ordered knots} (C{radians}).
450
439
  @kwarg name_wrap: Optional C{B{name}=NN} for this height interpolator
451
440
  (C{str}) and keyword argument C{b{wrap}=False} to wrap or
452
441
  I{normalize} all B{C{knots}} and B{C{llis}} locations iff
453
442
  C{True} (C{bool}).
454
443
 
455
- @raise HeightError: Insufficient number of B{C{knots}} or
456
- an invalid B{C{knot}} or B{C{weight}}.
444
+ @raise HeightError: Insufficient number of B{C{knots}} or an invalid
445
+ B{C{knot}}, B{C{weight}} or B{C{eps}}.
457
446
 
458
447
  @raise LenError: Unequal number of B{C{knots}} and B{C{weight}}s.
459
448
 
@@ -487,9 +476,10 @@ class HeightLSQBiSpline(_HeightsBase):
487
476
  w = Fmt.INDEX(weight=w.index(m))
488
477
  raise HeightError(w, m)
489
478
  try:
490
- T = 1.0e-4 # like SciPy example
491
- ps = np.array(_ordedup(xs, T, PI2 - T))
492
- ts = np.array(_ordedup(ys, T, PI - T))
479
+ if not EPS < low < (PI_2 - EPS): # 1e-4 like SciPy example
480
+ raise HeightError(low=low)
481
+ ps = np.array(_orderedup(xs, low, PI2 - low))
482
+ ts = np.array(_orderedup(ys, low, PI - low))
493
483
  self._ev = spi.LSQSphereBivariateSpline(ys, xs, hs,
494
484
  ts, ps, eps=EPS, w=w).ev
495
485
  except Exception as x:
pygeodesy/internals.py CHANGED
@@ -213,6 +213,13 @@ class _MODS_Base(object):
213
213
  from pygeodesy import streprs # DON'T _lazy_import2
214
214
  return streprs
215
215
 
216
+ @_Property_RO
217
+ def version(self):
218
+ '''Get pygeodesy version, I{once}.
219
+ '''
220
+ from pygeodesy import version
221
+ return version
222
+
216
223
  _MODS = _MODS_Base() # PYCHOK overwritten by .lazily
217
224
 
218
225
 
@@ -430,7 +437,7 @@ def _print7(nl=0, nt=0, prec=6, prefix=NN, sep=_SPACE_, file=_sys.stdout,
430
437
 
431
438
 
432
439
  def _Pythonarchine(sep=NN): # in .lazily, test/bases.py versions
433
- '''(INTERNAL) Get PyPy and Python versions, bit and machine as C{3- or 4-list} or C{str}.
440
+ '''(INTERNAL) Get PyPy and Python versions, bits and machine as C{3- or 4-list} or C{str}.
434
441
  '''
435
442
  l3 = _MODS.Pythonarchine
436
443
  return sep.join(l3) if sep else l3 # 3- or 4-list
@@ -576,16 +583,22 @@ def _version_ints(vs):
576
583
  return tuple(_ints(vs))
577
584
 
578
585
 
586
+ def _versions(sep=_SPACE_):
587
+ '''(INTERNAL) Get pygeodesy, PyPy and Python versions, bits, machine and OS as C{7- or 8-list} or C{str}.
588
+ '''
589
+ l7 = [_pygeodesy_, _MODS.version] + _Pythonarchine() + _osversion2()
590
+ return sep.join(l7) if sep else l7 # 5- or 6-list
591
+
592
+
579
593
  __all__ = tuple(map(_dunder_nameof, (machine, print_, printf)))
580
- __version__ = '24.07.04'
594
+ __version__ = '24.08.01'
581
595
 
582
596
  if _dunder_ismain(__name__): # PYCHOK no cover
583
597
 
584
- from pygeodesy import _isfrozen, isLazy, version as vs
598
+ from pygeodesy import _isfrozen, isLazy
585
599
 
586
- print_(_pygeodesy_, vs, *(_Pythonarchine() + _osversion2()
587
- + ['_isfrozen', _isfrozen,
588
- 'isLazy', isLazy]))
600
+ print_(*(_versions(sep=NN) + ['_isfrozen', _isfrozen,
601
+ 'isLazy', isLazy]))
589
602
 
590
603
  # **) MIT License
591
604
  #
pygeodesy/karney.py CHANGED
@@ -155,14 +155,14 @@ from pygeodesy.interns import NN, _2_, _a12_, _area_, _azi1_, _azi2_, _azi12_, \
155
155
  from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS, _getenv
156
156
  from pygeodesy.named import ADict, _NamedBase, _NamedTuple, notImplemented, _Pass
157
157
  from pygeodesy.props import deprecated_method, Property_RO, property_ROnce
158
- from pygeodesy.units import Bearing as _Azi, Degrees as _Deg, Lat, Lon, \
158
+ from pygeodesy.units import Azimuth as _Azi, Degrees as _Deg, Lat, Lon, \
159
159
  Meter as _M, Meter2 as _M2, Number_
160
160
  from pygeodesy.utily import atan2d, sincos2d, tand, _unrollon, fabs
161
161
 
162
162
  # from math import fabs # from .utily
163
163
 
164
164
  __all__ = _ALL_LAZY.karney
165
- __version__ = '24.07.16'
165
+ __version__ = '24.07.25'
166
166
 
167
167
  _K_2_0 = _getenv('PYGEODESY_GEOGRAPHICLIB', _2_) == _2_
168
168
  _perimeter_ = 'perimeter'
pygeodesy/latlonBase.py CHANGED
@@ -54,7 +54,7 @@ from contextlib import contextmanager
54
54
  from math import asin, cos, degrees, fabs, radians
55
55
 
56
56
  __all__ = _ALL_LAZY.latlonBase
57
- __version__ = '24.07.12'
57
+ __version__ = '24.07.29'
58
58
 
59
59
  _formy = _MODS.into(formy=__name__)
60
60
 
@@ -1534,7 +1534,7 @@ class LatLonBase(_NamedBase):
1534
1534
 
1535
1535
  @kwarg form: The lat-/longitude C{B{form}at} to use (C{str}), see
1536
1536
  functions L{pygeodesy.latDMS} or L{pygeodesy.lonDMS}.
1537
- @kwarg joined: Separator to join the lat-, longitude and heigth
1537
+ @kwarg joined: Separator to join the lat-, longitude and height
1538
1538
  strings (C{str} or C{None} or C{NN} for non-joined).
1539
1539
  @kwarg m: Optional unit of the height (C{str}), use C{None} to
1540
1540
  exclude height from the returned string.
pygeodesy/lazily.py CHANGED
@@ -31,8 +31,7 @@ from pygeodesy import internals as _internals, interns as _interns, \
31
31
  _isfrozen # DON'T _lazy_import2
32
32
  # from pygeodesy.errors import _error_init, _xkwds_item2 # _ALL_MODS
33
33
  from pygeodesy.internals import _caller3, _dunder_nameof, _dunder_ismain, \
34
- _headof, _osversion2, printf, _Pythonarchine, \
35
- _tailof # _Property_RO
34
+ _headof, printf, _Pythonarchine, _tailof # _Property_RO
36
35
  from pygeodesy.interns import NN, _attribute_, _by_, _COLONSPACE_, _COMMASPACE_, \
37
36
  _doesn_t_exist_, _DOT_, _EQUALSPACED_, _from_, \
38
37
  _HASH_, _immutable_, _line_, _module_, _no_, _not_, \
@@ -290,7 +289,7 @@ _ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY',
290
289
  geodesicx=_i('gx', 'gxarea', 'gxbases', 'gxline', # modules
291
290
  'GeodesicAreaExact', 'GeodesicExact', 'GeodesicLineExact', 'PolygonArea'),
292
291
  geodsolve=_i('GeodesicSolve', 'GeodesicLineSolve', 'GeodSolve12Tuple'),
293
- geohash=_i('Geohash', 'GeohashError', 'Neighbors8Dict', 'Resolutions2Tuple'),
292
+ geohash=_i('Geohash', 'Geohashed', 'GeohashError', 'Neighbors8Dict', 'Resolutions2Tuple', 'Sizes3Tuple'),
294
293
  geoids=_i('GeoidError', 'GeoidG2012B', 'GeoidKarney', 'GeoidPGM', 'egmGeoidHeights',
295
294
  'PGMError', 'GeoidHeight5Tuple'),
296
295
  hausdorff=_i('Hausdorff', 'HausdorffDegrees', 'HausdorffError', 'HausdorffRadians',
@@ -368,7 +367,7 @@ _ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY',
368
367
  triaxials=_i('BetaOmega2Tuple', 'BetaOmega3Tuple', 'Jacobi2Tuple',
369
368
  'JacobiConformal', 'JacobiConformalSpherical',
370
369
  'Triaxial', 'Triaxial_', 'TriaxialError', 'Triaxials', 'hartzell4'),
371
- units=_i('Band', 'Bearing', 'Bearing_', 'Bool',
370
+ units=_i('Azimuth', 'Band', 'Bearing', 'Bearing_', 'Bool',
372
371
  'Degrees', 'Degrees_', 'Degrees2', 'Distance', 'Distance_', 'Easting', 'Epoch',
373
372
  'Feet', 'FIx', 'Float_', 'Height', 'Height_', 'HeightX', 'Int_',
374
373
  'Lam', 'Lamd', 'Lat', 'Lat_', 'Lon', 'Lon_',
@@ -522,7 +521,7 @@ class _ALL_MODS(_internals._MODS_Base):
522
521
  _internals._MODS = _ALL_MODS = _ALL_MODS() # PYCHOK singleton
523
522
 
524
523
  __all__ = _ALL_LAZY.lazily
525
- __version__ = '24.07.18'
524
+ __version__ = '24.08.01'
526
525
 
527
526
 
528
527
  def _ALL_OTHER(*objs):
@@ -862,13 +861,13 @@ def _lazy_module(name): # overwritten by _lazy_import2
862
861
  #
863
862
  # return _ALL_OTHER(*sm.values())
864
863
 
865
-
866
864
  # del _i, _i0
867
865
 
868
866
  # _ = _ALL_MODS.errors # force import pygeodesy.errors
869
867
 
870
868
  if _dunder_ismain(__name__): # PYCHOK no cover
871
869
 
870
+ from pygeodesy.internals import _versions
872
871
  from timeit import timeit
873
872
 
874
873
  def t1():
@@ -882,22 +881,24 @@ if _dunder_ismain(__name__): # PYCHOK no cover
882
881
 
883
882
  t1 = timeit(t1, number=1000000)
884
883
  t2 = timeit(t2, number=1000000)
885
- v = _SPACE_.join(_Pythonarchine() + _osversion2())
886
- printf('%.6f import vs %.6f _ALL_MODS: %.2fX, %s', t1, t2, t1 / t2, v)
884
+ printf('%.6f import vs %.6f _ALL_MODS: %.2fX, %s', t1, t2, t1 / t2, _versions())
887
885
 
888
886
  # del t1, t2, timeit, v
889
887
 
890
- # python3.12 -W ignore -m pygeodesy.lazily
891
- # 0.145177 import vs 0.075402 _ALL_MODS: 1.93X, Python 3.12.3 64bit arm64 macOS 14.4.1
888
+ # % python3.13 -W ignore -m pygeodesy.lazily
889
+ # 0.102670 import vs 0.076113 _ALL_MODS: 1.35X, pygeodesy 24.8.4 Python 3.13.0b4 64bit arm64 macOS 14.5
890
+
891
+ # % python3.12 -W ignore -m pygeodesy.lazily
892
+ # 0.132801 import vs 0.079125 _ALL_MODS: 1.68X, pygeodesy 24.8.4 Python 3.12.4 64bit arm64 macOS 14.5
892
893
 
893
- # python3.11 -W ignore -m pygeodesy.lazily
894
- # 0.381723 import vs 0.251589 _ALL_MODS: 1.52X, Python 3.11.5 64bit arm64 macOS 14.4.1
894
+ # % python3.11 -W ignore -m pygeodesy.lazily
895
+ # 0.381723 import vs 0.251589 _ALL_MODS: 1.52X, pygeodesy 24.8.4 Python 3.11.5 64bit arm64 macOS 14.4.1
895
896
 
896
- # python3.10 -W ignore -m pygeodesy.lazily
897
- # 0.378293 import vs 0.266507 _ALL_MODS: 1.42X, Python 3.10.8 64bit arm64 macOS 14.4.1
897
+ # % python3.8 -W ignore -m pygeodesy.lazily
898
+ # 0.570353 import vs 0.382842 _ALL_MODS: 1.49X, pygeodesy 24.8.4 Python 3.8.10 64bit arm64_x86_64 macOS 10.16
898
899
 
899
- # python2 -m pygeodesy.lazily
900
- # 1.213805 import vs 0.474075 _ALL_MODS: 2.56X, Python 2.7.18 64bit arm64_x86_64 macOS 10.16
900
+ # % python2 -m pygeodesy.lazily
901
+ # 1.213805 import vs 0.474075 _ALL_MODS: 2.56X, pygeodesy 24.8.4 Python 2.7.18 64bit arm64_x86_64 macOS 10.16
901
902
 
902
903
  # **) MIT License
903
904
  #
pygeodesy/ltp.py CHANGED
@@ -44,7 +44,7 @@ from pygeodesy.vector3d import _ALL_LAZY, Vector3d
44
44
  # from math import fabs, floor as _floor # from .fmath, .fsums
45
45
 
46
46
  __all__ = _ALL_LAZY.ltp
47
- __version__ = '24.07.12'
47
+ __version__ = '24.07.25'
48
48
 
49
49
  _height0_ = _height_ + _0_
50
50
  _narrow_ = 'narrow'
@@ -730,7 +730,7 @@ class _ChLV(object):
730
730
  # assert _args_kwds_names( ChLV.reverse)[1:4] == t
731
731
  # assert _args_kwds_names(ChLVa.reverse)[1:4] == t
732
732
  # assert _args_kwds_names(ChLVe.reverse)[1:4] == t
733
- return t # overwrite propertyROver
733
+ return t # overwrite property_ROver
734
734
 
735
735
  def forward(self, latlonh, lon=None, height=0, M=None, **name): # PYCHOK no cover
736
736
  '''Convert WGS84 geodetic to I{Swiss} projection coordinates. I{Must be overloaded}.
pygeodesy/ltpTuples.py CHANGED
@@ -28,15 +28,15 @@ from pygeodesy.namedTuples import LatLon2Tuple, PhiLam2Tuple, Vector3Tuple
28
28
  from pygeodesy.props import deprecated_method, deprecated_Property_RO, \
29
29
  Property_RO, property_RO
30
30
  from pygeodesy.streprs import Fmt, fstr, strs, _xzipairs
31
- from pygeodesy.units import Bearing, Degrees, Degrees_, Height, _isDegrees, \
32
- _isMeter, Lat, Lon, Meter, Meter_
31
+ from pygeodesy.units import Azimuth, Bearing, Degrees, Degrees_, Height, \
32
+ _isDegrees, _isMeter, Lat, Lon, Meter, Meter_
33
33
  from pygeodesy.utily import atan2d, atan2b, sincos2_, sincos2d_, cos, radians
34
34
  from pygeodesy.vector3d import Vector3d
35
35
 
36
36
  # from math import cos, radians # from .utily
37
37
 
38
38
  __all__ = _ALL_LAZY.ltpTuples
39
- __version__ = '24.06.28'
39
+ __version__ = '24.07.25'
40
40
 
41
41
  _aer_ = 'aer'
42
42
  _alt_ = 'alt'
@@ -93,7 +93,7 @@ def _xyz2aer4(inst):
93
93
  '''(INTERNAL) Convert C{(x, y, z}) to C{(A, E, R)}.
94
94
  '''
95
95
  x, y, z, _ = inst.xyz4
96
- A = Bearing(azimuth=atan2b(x, y))
96
+ A = Azimuth(atan2b(x, y))
97
97
  E = Degrees(elevation=atan2d(z, hypot(x, y)))
98
98
  R = Meter(slantrange=hypot_(x, y, z))
99
99
  return Aer4Tuple(A, E, R, inst.ltp, name=inst.name)
@@ -268,7 +268,7 @@ class Aer(_AbcBase):
268
268
  '''
269
269
  if _isDegrees(azimuth_aer):
270
270
  aer = None
271
- t = (Bearing(azimuth=azimuth_aer),
271
+ t = (Azimuth(azimuth_aer),
272
272
  Degrees_(elevation=elevation, low=_N_90_0, high=_90_0),
273
273
  Meter_(slantrange=slantrange), ltp)
274
274
  else: # PYCHOK no cover
@@ -421,7 +421,7 @@ class Attitude4Tuple(_NamedTuple):
421
421
  the attitude of a plane or camera.
422
422
  '''
423
423
  _Names_ = (_alt_, _tilt_, _yaw_, _roll_)
424
- _Units_ = ( Meter, Bearing, Degrees, Degrees)
424
+ _Units_ = ( Meter, Degrees, Bearing, Degrees)
425
425
 
426
426
  @Property_RO
427
427
  def atyr(self):
pygeodesy/triaxials.py CHANGED
@@ -59,7 +59,7 @@ from pygeodesy.vector3d import _otherV3d, Vector3d, _ALL_LAZY, _MODS
59
59
  from math import atan2, fabs, sqrt
60
60
 
61
61
  __all__ = _ALL_LAZY.triaxials
62
- __version__ = '24.07.12'
62
+ __version__ = '24.07.29'
63
63
 
64
64
  _not_ordered_ = _not_('ordered')
65
65
  _omega_ = 'omega'
@@ -364,7 +364,7 @@ class Triaxial_(_NamedEnumItem):
364
364
  def _Elliptic(self):
365
365
  '''(INTERNAL) Get class L{Elliptic}, I{once}.
366
366
  '''
367
- return _MODS.elliptic.Elliptic # overwrite propertyROver
367
+ return _MODS.elliptic.Elliptic # overwrite property_ROver
368
368
 
369
369
  def hartzell4(self, pov, los=False, **name):
370
370
  '''Compute the intersection of this triaxial's surface with a Line-Of-Sight
@@ -753,7 +753,7 @@ class Triaxial(Triaxial_):
753
753
  return self.height4(x_xyz, y, z, **normal_eps_name)
754
754
 
755
755
  def forwardLatLon(self, lat, lon, height=0, **name):
756
- '''Convert I{geodetic} lat-, longitude and heigth to cartesian.
756
+ '''Convert I{geodetic} lat-, longitude and height to cartesian.
757
757
 
758
758
  @arg lat: Geodetic latitude (C{degrees}).
759
759
  @arg lon: Geodetic longitude (C{degrees}).
@@ -769,7 +769,7 @@ class Triaxial(Triaxial_):
769
769
  return self._forwardLatLon3(height, name, *sincos2d_(lat, lon))
770
770
 
771
771
  def forwardLatLon_(self, slat, clat, slon, clon, height=0, **name):
772
- '''Convert I{geodetic} lat-, longitude and heigth to cartesian.
772
+ '''Convert I{geodetic} lat-, longitude and height to cartesian.
773
773
 
774
774
  @arg slat: Geodetic latitude C{sin(lat)} (C{scalar}).
775
775
  @arg clat: Geodetic latitude C{cos(lat)} (C{scalar}).
pygeodesy/units.py CHANGED
@@ -11,11 +11,11 @@ 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.interns import NN, _band_, _bearing_, _COMMASPACE_, _degrees_, \
15
- _degrees2_, _distance_, _E_, _easting_, _epoch_, _EW_, \
16
- _feet_, _height_, _lam_, _lat_, _LatLon_, _lon_, \
17
- _meter_, _meter2_, _N_, _negative_, _northing_, _NS_, \
18
- _NSEW_, _number_, _PERCENT_, _phi_, _precision_, \
14
+ from pygeodesy.interns import NN, _azimuth_, _band_, _bearing_, _COMMASPACE_, \
15
+ _degrees_, _degrees2_, _distance_, _E_, _easting_, \
16
+ _epoch_, _EW_, _feet_, _height_, _lam_, _lat_, _LatLon_, \
17
+ _lon_, _meter_, _meter2_, _N_, _negative_, _northing_, \
18
+ _NS_, _NSEW_, _number_, _PERCENT_, _phi_, _precision_, \
19
19
  _radians_, _radians2_, _radius_, _S_, _scalar_, \
20
20
  _units_, _W_, _zone_, _std_ # PYCHOK used!
21
21
  from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS, _getenv
@@ -24,10 +24,10 @@ from pygeodesy.props import Property_RO
24
24
  # from pygeodesy.streprs import Fmt, fstr # from .unitsBase
25
25
  from pygeodesy.unitsBase import Float, Int, _NamedUnit, Radius, Str, Fmt, fstr
26
26
 
27
- from math import degrees, radians
27
+ from math import degrees, isnan, radians
28
28
 
29
29
  __all__ = _ALL_LAZY.units
30
- __version__ = '24.06.29'
30
+ __version__ = '24.08.02'
31
31
 
32
32
 
33
33
  class Float_(Float):
@@ -339,18 +339,35 @@ class Radians2(Float_):
339
339
  return Float_.__new__(cls, arg=arg, name=name, low=_0_0, **Error_name_arg)
340
340
 
341
341
 
342
+ def _Degrees_new(cls, **arg_name_suffix_clip_Error_name_arg):
343
+ d = Degrees.__new__(cls, **arg_name_suffix_clip_Error_name_arg)
344
+ b = _umod_360(d) # 0 <= b < 360
345
+ return d if b == d else Degrees.__new__(cls, arg=b, name=d.name)
346
+
347
+
348
+ class Azimuth(Degrees):
349
+ '''Named C{float} representing an azimuth in compass C{degrees} from (true) North.
350
+ '''
351
+ _ddd_ = 1
352
+ _suf_ = _W_, S_NUL, _E_ # no zero suffix
353
+
354
+ def __new__(cls, arg=None, name=_azimuth_, **clip_Error_name_arg):
355
+ '''New, named L{Azimuth} with optional suffix 'E' for clockwise or 'W' for
356
+ anti-clockwise, see L{Degrees}.
357
+ '''
358
+ return _Degrees_new(cls, arg=arg, name=name, suffix=_EW_, **clip_Error_name_arg)
359
+
360
+
342
361
  class Bearing(Degrees):
343
362
  '''Named C{float} representing a bearing in compass C{degrees} from (true) North.
344
363
  '''
345
364
  _ddd_ = 1
346
365
  _suf_ = _N_ * 3 # always suffix N
347
366
 
348
- def __new__(cls, arg=None, name=_bearing_, clip=0, **Error_name_arg):
367
+ def __new__(cls, arg=None, name=_bearing_, **clip_Error_name_arg):
349
368
  '''New, named L{Bearing}, see L{Degrees}.
350
369
  '''
351
- d = Degrees.__new__(cls, arg=arg, name=name, suffix=_N_, clip=clip, **Error_name_arg)
352
- b = _umod_360(d) # 0 <= b < 360
353
- return d if b == d else Degrees.__new__(cls, arg=b, name=d.name)
370
+ return _Degrees_new(cls, arg=arg, name=name, suffix=_N_, **clip_Error_name_arg)
354
371
 
355
372
 
356
373
  class Bearing_(Radians):
@@ -782,7 +799,7 @@ class Zone(Int):
782
799
 
783
800
 
784
801
  _ScalarU = Float, Float_, Scalar, Scalar_
785
- _Degrees = (Bearing, Bearing_, Degrees, Degrees_) + _ScalarU
802
+ _Degrees = (Azimuth, Bearing, Bearing_, Degrees, Degrees_) + _ScalarU
786
803
  _Meters = (Distance, Distance_, Meter, Meter_) + _ScalarU
787
804
  _Radians = (Radians, Radians_) + _ScalarU # PYCHOK unused
788
805
  _Radii = _Meters + (Radius, Radius_)
@@ -794,7 +811,7 @@ def _isDegrees(obj):
794
811
 
795
812
 
796
813
  def _isHeight(obj):
797
- # Check for valid heigth types.
814
+ # Check for valid height types.
798
815
  return isinstance(obj, _Meters) or _isScalar(obj)
799
816
 
800
817
 
@@ -823,13 +840,13 @@ def _toUnit(Unit, arg, name=NN, **Error):
823
840
 
824
841
 
825
842
  def _xlimits(arg, low, high, g=False):
826
- '''(INTERNAL) Check C{low <= unit <= high}.
843
+ '''(INTERNAL) Check C{low <= arg <= high}.
827
844
  '''
828
- if (low is not None) and arg < low:
845
+ if (low is not None) and (arg < low or isnan(arg)):
829
846
  if g:
830
847
  low = Fmt.g(low, prec=6, ints=isinstance(arg, Epoch))
831
848
  t = Fmt.limit(below=low)
832
- elif (high is not None) and arg > high:
849
+ elif (high is not None) and (arg > high or isnan(arg)):
833
850
  if g:
834
851
  high = Fmt.g(high, prec=6, ints=isinstance(arg, Epoch))
835
852
  t = Fmt.limit(above=high)
@@ -847,7 +864,7 @@ def _std_repr(*Classes):
847
864
  if _getenv(env, _std_).lower() != _std_:
848
865
  C._std_repr = False
849
866
 
850
- _std_repr(Bearing, Bool, Degrees, Float, Int, Meter, Radians, Str) # PYCHOK expected
867
+ _std_repr(Azimuth, Bearing, Bool, Degrees, Float, Int, Meter, Radians, Str) # PYCHOK expected
851
868
  del _std_repr
852
869
 
853
870
  __all__ += _ALL_DOCS(_NamedUnit)