pygeodesy 25.8.25__py2.py3-none-any.whl → 25.10.10__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 (48) hide show
  1. pygeodesy/__init__.py +21 -20
  2. pygeodesy/auxilats/__init__.py +1 -1
  3. pygeodesy/auxilats/auxAngle.py +4 -3
  4. pygeodesy/auxilats/auxily.py +1 -1
  5. pygeodesy/azimuthal.py +10 -12
  6. pygeodesy/basics.py +4 -4
  7. pygeodesy/booleans.py +25 -25
  8. pygeodesy/constants.py +59 -33
  9. pygeodesy/deprecated/functions.py +1 -0
  10. pygeodesy/dms.py +2 -2
  11. pygeodesy/ecef.py +3 -3
  12. pygeodesy/ellipsoidalExact.py +4 -4
  13. pygeodesy/ellipsoidalGeodSolve.py +3 -3
  14. pygeodesy/ellipsoids.py +52 -41
  15. pygeodesy/elliptic.py +9 -12
  16. pygeodesy/errors.py +18 -5
  17. pygeodesy/etm.py +10 -10
  18. pygeodesy/fmath.py +5 -3
  19. pygeodesy/geodesicx/__init__.py +1 -1
  20. pygeodesy/geodesicx/__main__.py +1 -0
  21. pygeodesy/geodesicx/gx.py +40 -46
  22. pygeodesy/geodesicx/gxarea.py +4 -4
  23. pygeodesy/geodesicx/gxbases.py +1 -5
  24. pygeodesy/geodesicx/gxline.py +43 -34
  25. pygeodesy/geodsolve.py +10 -17
  26. pygeodesy/geohash.py +6 -6
  27. pygeodesy/geoids.py +2 -2
  28. pygeodesy/heights.py +2 -2
  29. pygeodesy/internals.py +42 -19
  30. pygeodesy/karney.py +27 -26
  31. pygeodesy/ktm.py +1 -1
  32. pygeodesy/lazily.py +12 -11
  33. pygeodesy/lcc.py +5 -5
  34. pygeodesy/named.py +11 -14
  35. pygeodesy/rhumb/__init__.py +1 -1
  36. pygeodesy/rhumb/aux_.py +1 -1
  37. pygeodesy/rhumb/bases.py +7 -8
  38. pygeodesy/rhumb/ekx.py +9 -9
  39. pygeodesy/solveBase.py +14 -3
  40. pygeodesy/sphericalTrigonometry.py +4 -4
  41. pygeodesy/streprs.py +9 -9
  42. pygeodesy/trf.py +4 -4
  43. pygeodesy/utily.py +200 -159
  44. pygeodesy/vector3dBase.py +6 -6
  45. {pygeodesy-25.8.25.dist-info → pygeodesy-25.10.10.dist-info}/METADATA +21 -20
  46. {pygeodesy-25.8.25.dist-info → pygeodesy-25.10.10.dist-info}/RECORD +48 -48
  47. {pygeodesy-25.8.25.dist-info → pygeodesy-25.10.10.dist-info}/WHEEL +0 -0
  48. {pygeodesy-25.8.25.dist-info → pygeodesy-25.10.10.dist-info}/top_level.txt +0 -0
pygeodesy/internals.py CHANGED
@@ -4,10 +4,10 @@
4
4
  u'''Mostly INTERNAL functions, except L{machine}, L{print_} and L{printf}.
5
5
  '''
6
6
  # from pygeodesy.basics import isiterablen, ubstr # _MODS
7
- # from pygeodesy.errors import _AttributeError, _error_init, _UnexpectedError, _xError2 # _MODS
8
- from pygeodesy.interns import _BAR_, _COLON_, _DASH_, _DMAIN_, _DOT_, _ELLIPSIS_, _EQUALSPACED_, \
9
- _immutable_, _NL_, NN, _pygeodesy_, _PyPy__, _python_, _QUOTE1_, \
10
- _QUOTE2_, _s_, _SPACE_, _sys, _UNDER_
7
+ # from pygeodesy.errors import _AttributeError, _error_init, _ImmutableError, _UnexpectedError, _xError2 # _MODS
8
+ from pygeodesy.interns import _BAR_, _COLON_, _DASH_, _DMAIN_, _DOT_, _ELLIPSIS_, _NL_, NN, \
9
+ _pygeodesy_, _PyPy__, _python_, _QUOTE1_, _QUOTE2_, _s_, _sys, \
10
+ _SPACE_, _UNDER_
11
11
  from pygeodesy.interns import _COMMA_, _Python_ # PYCHOK used!
12
12
  # from pygeodesy.streprs import anstr, pairs, unstr # _MODS
13
13
 
@@ -56,6 +56,27 @@ def _Property_RO(method):
56
56
  return property(_get, _set, _del)
57
57
 
58
58
 
59
+ class _Enum(object): # in .elliptic, .utily
60
+ '''(INTERNAL) Enum-like, immutable items.
61
+ '''
62
+ # _ImmutableError = None
63
+
64
+ def __init__(self, **enums):
65
+ self.__dict__.update(enums)
66
+ # for item in enums.items():
67
+ # setattr(self, *item) # object.__setattr__
68
+
69
+ def __str__(self):
70
+ _unstr = _MODS.streprs.unstr
71
+ return _unstr(_Enum, **self.__dict__)
72
+
73
+ # def __delattr__(self, attr): # PYCHOK no cover
74
+ # raise _ImmutableError(self, attr) # _del_
75
+
76
+ # def __setattr__(self, attr, value): # PYCHOK no cover
77
+ # raise _ImmutableError(self, attr, value)
78
+
79
+
59
80
  class _MODS_Base(object):
60
81
  '''(INTERNAL) Base-class for C{lazily._ALL_MODS}.
61
82
  '''
@@ -63,10 +84,7 @@ class _MODS_Base(object):
63
84
  self.__dict__.pop(attr, None)
64
85
 
65
86
  def __setattr__(self, attr, value): # PYCHOK no cover
66
- e = _MODS.errors
67
- n = _DOT_(self.name, attr)
68
- t = _EQUALSPACED_(n, repr(value))
69
- raise e._AttributeError(_immutable_, txt=t)
87
+ raise _ImmutableError(self, attr, value)
70
88
 
71
89
  @_Property_RO
72
90
  def basics(self):
@@ -184,7 +202,7 @@ class _MODS_Base(object):
184
202
  (_macOS_, p.mac_ver),
185
203
  (_Windows_, p.win32_ver),
186
204
  (_Nix, _MODS.nix_ver),
187
- ('Java', p.java_ver),
205
+ # removed Py 3.15 ('Java', p.java_ver),
188
206
  ('uname', p.uname)):
189
207
  v = v()[0]
190
208
  if v and n:
@@ -302,6 +320,12 @@ def _headof(name):
302
320
  return name if i < 0 else name[:i]
303
321
 
304
322
 
323
+ def _ImmutableError(*inst_attr_value):
324
+ '''(INTERNAL) Format an C{_ImmutableError}.
325
+ '''
326
+ return _MODS.errors._ImmutableError(*inst_attr_value)
327
+
328
+
305
329
  # def _is(a, b): # PYCHOK no cover
306
330
  # '''(INTERNAL) C{a is b}? in C{PyPy}
307
331
  # '''
@@ -473,10 +497,10 @@ def printf(fmt, *args, **nl_nt_prec_prefix__end_file_flush_sep__kwds):
473
497
  else:
474
498
  t = fmt
475
499
  except Exception as x:
476
- _E, s = _MODS.errors._xError2(x)
477
- unstr = _MODS.streprs.unstr
478
- t = unstr(printf, fmt, *args, **nl_nt_prec_prefix__end_file_flush_sep__kwds)
479
- raise _E(s, txt=t, cause=x)
500
+ _Error, s = _MODS.errors._xError2(x)
501
+ _unstr = _MODS.strepr.unstr
502
+ t = _unstr(printf, fmt, *args, **nl_nt_prec_prefix__end_file_flush_sep__kwds)
503
+ raise _Error(s, txt=t, cause=x)
480
504
  try:
481
505
  n = f.write(NN(b, t, e))
482
506
  except UnicodeEncodeError: # XXX only Windows
@@ -635,11 +659,10 @@ def _usage_argv(argv0, *args):
635
659
  '''(INTERNAL) Return 3-tuple C{(python, '-m', module, *args)}.
636
660
  '''
637
661
  o = _MODS.os
638
- m = o.path.dirname(argv0)
639
- m = m.replace(o.getcwd(), _ELLIPSIS_) \
640
- .replace(o.sep, _DOT_).strip()
641
- b = o.path.basename(argv0)
642
- b, x = o.path.splitext(b)
662
+ p = o.path
663
+ m = p.dirname(argv0).replace(o.getcwd(), _ELLIPSIS_) \
664
+ .replace(o.sep, _DOT_).strip()
665
+ b, x = p.splitext(p.basename(argv0))
643
666
  if x == '.py' and b != _DMAIN_:
644
667
  m = _DOT_(m or _pygeodesy_, b)
645
668
  p = NN(_python_, _MODS.sys_version_info2[0])
@@ -686,7 +709,7 @@ def _versions(sep=_SPACE_):
686
709
 
687
710
 
688
711
  __all__ = tuple(map(typename, (machine, print_, printf, typename)))
689
- __version__ = '25.08.18'
712
+ __version__ = '25.10.06'
690
713
 
691
714
  if __name__ == _DMAIN_:
692
715
 
pygeodesy/karney.py CHANGED
@@ -145,8 +145,8 @@ from __future__ import division as _; del _ # noqa: E702 ;
145
145
 
146
146
  from pygeodesy.basics import _copysign, _isin, isint, neg, unsigned0, \
147
147
  _xgeographiclib, _zip
148
- from pygeodesy.constants import NAN, _isfinite as _math_isfinite, _0_0, \
149
- _1_16th, _1_0, _2_0, _180_0, _N_180_0, _360_0
148
+ from pygeodesy.constants import NAN, _isfinite as _math_isfinite, \
149
+ _0_0, _1_0, _2_0, _180_0, _N_180_0, _360_0
150
150
  from pygeodesy.errors import GeodesicError, _ValueError, _xkwds
151
151
  from pygeodesy.fmath import cbrt, fremainder, norm2 # Fhorner, Fsum
152
152
  from pygeodesy.internals import _getenv, _popen2, _PYGEODESY_ENV, typename, \
@@ -157,8 +157,7 @@ from pygeodesy.interns import NN, _a12_, _area_, _azi1_, _azi2_, _azi12_, \
157
157
  _SPACE_, _UNDER_, _X_, _1_, _2_, _BAR_ # PYCHOK used!
158
158
  from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS, _FOR_DOCS
159
159
  from pygeodesy.named import ADict, _NamedBase, _NamedTuple, notImplemented, _Pass
160
- from pygeodesy.props import deprecated_method, Property_RO, property_RO, \
161
- property_ROnce
160
+ from pygeodesy.props import deprecated_method, property_RO, property_ROnce
162
161
  from pygeodesy.units import Azimuth as _Azi, Degrees as _Deg, Lat, Lon, \
163
162
  Meter as _M, Meter2 as _M2, Number_
164
163
  from pygeodesy.utily import atan2d, sincos2d, tand, _unrollon, fabs
@@ -166,8 +165,9 @@ from pygeodesy.utily import atan2d, sincos2d, tand, _unrollon, fabs
166
165
  # from math import fabs # from .utily
167
166
 
168
167
  __all__ = _ALL_LAZY.karney
169
- __version__ = '25.05.28'
168
+ __version__ = '25.09.13'
170
169
 
170
+ _1_16th = _1_0 / 16
171
171
  _2_4_ = '2.4'
172
172
  _K_2_0 = _getenv(_PYGEODESY_ENV(typename(_xgeographiclib)[2:]), _2_)
173
173
  _K_2_4 = _K_2_0 == _2_4_
@@ -270,16 +270,16 @@ class Caps(object):
270
270
  REDUCEDLENGTH = 1 << 12 | _CAP_1 | _CAP_2 # compute reduced length C{m12}
271
271
  GEODESICSCALE = 1 << 13 | _CAP_1 | _CAP_2 # compute geodesic scales C{M12} and C{M21}
272
272
  AREA = 1 << 14 | _CAP_4 # compute area C{S12}
273
+ ALL = 0x7F80 | _CAP_ALL # without LONG_UNROLL, LINE_OFF, NONFINITONAN, REVERSE2 and _DEBUG_*
273
274
 
274
275
  STANDARD = AZIMUTH | DISTANCE | LATITUDE | LONGITUDE
275
276
  STANDARD_LINE = STANDARD | DISTANCE_IN # for goedesici/-w
276
277
 
277
278
  LINE_CAPS = STANDARD_LINE | REDUCEDLENGTH | GEODESICSCALE # .geodesici only
278
279
  LONG_UNROLL = 1 << 15 # unroll C{lon2} in .Direct and .Position
279
- NONFINITONAN = 1 << 16 # see method GDict._toNAN
280
- LINE_OFF = 1 << 17 # Line without updates from parent geodesic or rhumb
280
+ LINE_OFF = 1 << 16 # Line without updates from parent geodesic or rhumb
281
+ NONFINITONAN = 1 << 17 # see method GDict._toNAN
281
282
  REVERSE2 = 1 << 18 # reverse C{azi2}
282
- ALL = 0x7F80 | _CAP_ALL # without LONG_UNROLL, LINE_OFF, REVERSE2 and _DEBUG_*
283
283
 
284
284
  AZIMUTH_DISTANCE = AZIMUTH | DISTANCE
285
285
  AZIMUTH_DISTANCE_AREA = AZIMUTH | DISTANCE | AREA
@@ -300,7 +300,7 @@ class Caps(object):
300
300
  _INVERSE3 = AZIMUTH | DISTANCE # for goedesicw only
301
301
 
302
302
  _OUT_ALL = ALL # see geographiclib.geodesiccapabilities.py
303
- _OUT_MASK = ALL | LONG_UNROLL | REVERSE2 | _DEBUG_ALL
303
+ _OUT_MASK = ALL | LONG_UNROLL | NONFINITONAN | REVERSE2 | _DEBUG_ALL
304
304
 
305
305
  _AZIMUTH_LATITUDE_LONGITUDE = AZIMUTH | LATITUDE | LONGITUDE
306
306
  _AZIMUTH_LATITUDE_LONG_UNROLL = AZIMUTH | LATITUDE | LONG_UNROLL
@@ -366,7 +366,7 @@ class _CapsBase(_NamedBase): # in .auxilats, .geodesicx.gxbases
366
366
  _caps = 0 # None
367
367
  _debug = 0 # or Caps._DEBUG_...
368
368
 
369
- @Property_RO
369
+ @property_RO
370
370
  def caps(self):
371
371
  '''Get the capabilities (bit-or'ed C{Caps}).
372
372
  '''
@@ -455,13 +455,16 @@ class GDict(ADict): # XXX _NamedDict
455
455
  '''
456
456
  return self._toTuple(Inverse10Tuple, dflt)
457
457
 
458
- def _toNAN(self, outmask): # .GeodesicExact._GDistInverse, .GeodesicLineExact._GenPosition
458
+ def _toNAN(self, outmask, **specs): # .GeodesicExact._GDistInverse, .GeodesicLineExact._GenPosition
459
459
  '''(INTERNAL) Convert this C{GDict} to all C{NAN}s.
460
460
  '''
461
461
  if (outmask & Caps.NONFINITONAN):
462
- d = dict((k, NAN) for k, C in _key2Caps.items()
463
- if (outmask & C) == C)
464
- self.set_(**d)
462
+ def _t2(k):
463
+ return k, specs.get(k, NAN)
464
+
465
+ d = dict(_t2(k) for k, C in _key2Caps.items()
466
+ if (outmask & C) == C)
467
+ self.set_(**d) # self.update(d)
465
468
  return self
466
469
 
467
470
  @deprecated_method
@@ -721,12 +724,10 @@ def _around(x): # in .utily.sincos2d
721
724
  try:
722
725
  return _wrapped.Math.AngRound(x)
723
726
  except AttributeError:
724
- if x:
725
- y = _1_16th - fabs(x)
726
- if y > 0: # fabs(x) < _1_16th
727
- x = _copysign(_1_16th - y, x)
728
- else:
729
- x = _0_0 # -0 to 0
727
+ z = _1_16th
728
+ w = z - fabs(x)
729
+ if w > 0: # don't "simplify" z - (z - x) to x
730
+ x = _copysign(z - w, x)
730
731
  return x
731
732
 
732
733
 
@@ -771,13 +772,13 @@ def _diff182(deg0, deg, K_2_0=False):
771
772
  try:
772
773
  return _wrapped.Math.AngDiff(deg0, deg)
773
774
  except AttributeError:
774
- if K_2_0 or _K_2_0: # geographiclib 2.0
775
- _r, _360 = fremainder, _360_0
776
- d, t = _sum2(_r(-deg0, _360),
777
- _r( deg, _360))
778
- d, t = _sum2(_r( d, _360), t)
775
+ if K_2_0 or _K_2_0: # geographiclib 2.0+
776
+ _r = fremainder
777
+ d, t = _sum2(_r(-deg0, _360_0),
778
+ _r( deg, _360_0))
779
+ d, t = _sum2(_r( d, _360_0), t)
779
780
  if _isin(d, _0_0, _180_0, _N_180_0):
780
- d = _copysign(d, -t if t else (deg - deg0))
781
+ d = _copysign(d, (-t) if t else (deg - deg0))
781
782
  else:
782
783
  _n = _norm180
783
784
  d, t = _sum2(_n(-deg0), _n(deg))
pygeodesy/ktm.py CHANGED
@@ -66,7 +66,7 @@ from cmath import polar as _polar
66
66
  from math import asinh, cos, cosh, degrees, fabs, sin, sinh, sqrt, tanh
67
67
 
68
68
  __all__ = _ALL_LAZY.ktm
69
- __version__ = '25.08.18'
69
+ __version__ = '25.08.31'
70
70
 
71
71
 
72
72
  class KTMError(_ValueError):
pygeodesy/lazily.py CHANGED
@@ -29,15 +29,15 @@ and line number.
29
29
 
30
30
  from pygeodesy import internals as _internals, interns as _interns, \
31
31
  _isfrozen # DON'T _lazy_import2
32
- # from pygeodesy.errors import _error_init, _xkwds_item2 # _ALL_MODS
32
+ # from pygeodesy.errors import _error_init, _ImmutableError, _xkwds_item2 # _ALL_MODS
33
33
  from pygeodesy.internals import _caller3, _envPYGEODESY, _headof, printf, _tailof, \
34
34
  typename, _versions # _getenv, _PYGEODESY_ENV, \
35
35
  # _MODS_Base, _MODS.sys_version_info2
36
36
  from pygeodesy.interns import _attribute_, _by_, _COLONSPACE_, _COMMASPACE_, _DALL_, \
37
37
  _DMAIN_, _doesn_t_exist_, _DOT_, _EQUALSPACED_, _from_, \
38
- _HASH_, _immutable_, _line_, _module_, NN, _no_, _not_, \
39
- _pygeodesy_, _pygeodesy_abspath_, _SPACE_, _SUB_PACKAGES, \
40
- _or_, _UNDER_, _version_, _sys, _intern # function, _1_
38
+ _HASH_, _line_, _module_, NN, _no_, _not_, _pygeodesy_, \
39
+ _pygeodesy_abspath_, _SPACE_, _SUB_PACKAGES, _or_, \
40
+ _UNDER_, _version_, _sys, _intern # function, _1_
41
41
  try:
42
42
  from importlib import import_module
43
43
  except ImportError as x: # Python 2.6-
@@ -125,8 +125,9 @@ class _NamedEnum_RO(dict):
125
125
  raise LazyAttributeError(t, txt=_doesn_t_exist_)
126
126
 
127
127
  def __setattr__(self, attr, value): # PYCHOK no cover
128
- t = _EQUALSPACED_(self._DOT_(attr), repr(value))
129
- raise LazyAttributeError(_immutable_, txt=t)
128
+ e = _ALL_MODS.errors
129
+ raise e._ImmutableError(self, attr, value,
130
+ Error=LazyAttributeError)
130
131
 
131
132
  def enums(self):
132
133
  # Yield all C{(mod_, tuple)} pairs
@@ -200,10 +201,10 @@ _ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY',
200
201
  'EasNorAziRk4Tuple', 'EasNorAziRkEqu6Tuple', 'LatLonAziRk4Tuple'),
201
202
  constants=_a('DIG', 'EPS', 'EPS0', 'EPS02', 'EPS1', 'EPS2', 'EPS4', 'EPS_2',
202
203
  'INF', 'INT0', 'MANT_DIG', 'MAX', 'MAX_EXP', 'MIN', 'MIN_EXP', 'NAN', 'NEG0', 'NINF',
203
- 'PI', 'PI2', 'PI_2', 'PI3', 'PI_3', 'PI3_2', 'PI4', 'PI_4',
204
+ 'PI', 'PI2', 'PI_2', 'PI3', 'PI_3', 'PI3_2', 'PI4', 'PI_4', 'PI_6',
204
205
  'R_FM', 'R_GM', 'R_KM', 'R_M', 'R_MA', 'R_MB', 'R_NM', 'R_QM', 'R_SM', 'R_VM',
205
206
  'float_', 'float0_', 'isclose', 'isfinite', 'isinf', 'isint0',
206
- 'isnan', 'isnear0', 'isnear1', 'isnear90', 'isneg0', 'isninf', 'isnon0',
207
+ 'isnan', 'isnear0', 'isnear1', 'isnear90', 'isneg', 'isneg0', 'isninf', 'isnon0',
207
208
  'remainder'),
208
209
  datums=_a('Datum', 'Datums', 'Transform', 'Transforms'),
209
210
  # deprecated=_a(), # module only
@@ -357,11 +358,11 @@ _ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY',
357
358
  'Radius_', 'Scalar', 'Scalar_', 'Zone'),
358
359
  unitsBase=_a('Float', 'Int', 'Radius', 'Str'),
359
360
  ups=_a('Ups', 'UPSError', 'parseUPS5', 'toUps8', 'upsZoneBand5'),
360
- utily=_a('acos1', 'acre2ha', 'acre2m2', 'asin1', 'atan1', 'atan1d', 'atan2', 'atan2b', 'atan2d',
361
+ utily=_a('acos1', 'acre2ha', 'acre2m2', 'agdf', 'asin1', 'atan1', 'atan1d', 'atan2', 'atan2b', 'atan2d',
361
362
  'chain2m', 'circle4', 'cot', 'cot_', 'cotd', 'cotd_',
362
363
  'degrees', 'degrees90', 'degrees180', 'degrees360', 'degrees2grades', 'degrees2m',
363
364
  'fathom2m', 'ft2m', 'furlong2m', # 'degrees2grades as degrees2gons',
364
- 'grades', 'grades400', 'grades2degrees', 'grades2radians',
365
+ 'gdf', 'grades', 'grades400', 'grades2degrees', 'grades2radians',
365
366
  # 'grades as gons', 'grades400 as gons400', 'grades2degrees as gons2degrees', 'grades2radians as gons2radians',
366
367
  'ha2acre', 'ha2m2', 'hav', 'km2m',
367
368
  'm2acre', 'm2chain', 'm2degrees', 'm2fathom', 'm2ft', 'm2furlong',
@@ -510,7 +511,7 @@ class _ALL_MODS(_internals._MODS_Base):
510
511
  _internals._MODS = _ALL_MODS = _ALL_MODS() # PYCHOK singleton
511
512
 
512
513
  __all__ = _ALL_LAZY.lazily
513
- __version__ = '25.08.22'
514
+ __version__ = '25.09.09'
514
515
 
515
516
 
516
517
  def _ALL_OTHER(*objs):
pygeodesy/lcc.py CHANGED
@@ -24,10 +24,10 @@ and John P. Snyder U{'Map Projections - A Working Manual'<https://Pubs.USGS.gov/
24
24
  # make sure int/int division yields float quotient, see .basics
25
25
  from __future__ import division as _; del _ # noqa: E702 ;
26
26
 
27
- from pygeodesy.basics import copysign0, _isin, _xinstanceof, _xsubclassof, \
28
- typename
29
- from pygeodesy.constants import EPS, EPS02, PI_2, _float as _F, _0_0, _0_5, \
30
- _1_0, _2_0, _90_0
27
+ from pygeodesy.basics import copysign0, _isin, typename, \
28
+ _xinstanceof, _xsubclassof
29
+ from pygeodesy.constants import EPS, EPS02, PI_2, _float as _F, \
30
+ _0_0, _0_5, _1_0, _2_0, _90_0
31
31
  from pygeodesy.ellipsoidalBase import LatLonEllipsoidalBase as _LLEB
32
32
  from pygeodesy.datums import Datums, _ellipsoidal_datum
33
33
  from pygeodesy.errors import _IsnotError, _ValueError
@@ -50,7 +50,7 @@ from pygeodesy.utily import atan1, degrees90, degrees180, sincos2, tanPI_2_2
50
50
  from math import atan, fabs, log, radians, sin, sqrt
51
51
 
52
52
  __all__ = _ALL_LAZY.lcc
53
- __version__ = '25.05.26'
53
+ __version__ = '25.08.31'
54
54
 
55
55
  _E0_ = 'E0'
56
56
  _N0_ = 'N0'
pygeodesy/named.py CHANGED
@@ -16,17 +16,17 @@ standard Python C{namedtuple}s.
16
16
  from pygeodesy.basics import isbool, isidentifier, iskeyword, isstr, itemsorted, \
17
17
  len2, _xcopy, _xdup, _xinstanceof, _xsubclassof, _zip
18
18
  # from pygeodesy.ecef import EcefKarney # _MODS
19
- from pygeodesy.errors import _AssertionError, _AttributeError, _incompatible, \
20
- _IndexError, _KeyError, LenError, _NameError, \
21
- _NotImplementedError, _TypeError, _TypesError, \
22
- _UnexpectedError, UnitError, _ValueError, \
19
+ from pygeodesy.errors import _AssertionError, _AttributeError, _ImmutableError, \
20
+ _incompatible, _IndexError, _KeyError, LenError, \
21
+ _NameError, _NotImplementedError, _TypeError, \
22
+ _TypesError, _UnexpectedError, UnitError, _ValueError, \
23
23
  _xattr, _xkwds, _xkwds_item2, _xkwds_pop2
24
24
  from pygeodesy.internals import _caller3, _envPYGEODESY, _isPyPy, _sizeof, \
25
25
  typename, _under
26
26
  from pygeodesy.interns import MISSING, NN, _AT_, _COLON_, _COLONSPACE_, _COMMA_, \
27
27
  _COMMASPACE_, _DNAME_, _doesn_t_exist_, _DOT_, _DUNDER_, \
28
- _EQUAL_, _exists_, _immutable_, _name_, _NL_, _NN_, \
29
- _no_, _other_, _s_, _SPACE_, _std_, _UNDER_, _vs_
28
+ _EQUAL_, _exists_, _name_, _NL_, _NN_, _no_, _other_, \
29
+ _s_, _SPACE_, _std_, _UNDER_, _vs_
30
30
  from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS
31
31
  from pygeodesy.props import _allPropertiesOf_n, deprecated_method, _hasProperty, \
32
32
  _update_all, property_doc_, Property_RO, property_RO, \
@@ -35,11 +35,10 @@ from pygeodesy.streprs import attrs, Fmt, lrstrip, pairs, reprs, unstr
35
35
  # from pygeodesy.units import _toUnit # _MODS
36
36
 
37
37
  __all__ = _ALL_LAZY.named
38
- __version__ = '25.04.28'
38
+ __version__ = '25.09.04'
39
39
 
40
40
  _COMMANL_ = _COMMA_ + _NL_
41
41
  _COMMASPACEDOT_ = _COMMASPACE_ + _DOT_
42
- _del_ = 'del'
43
42
  _item_ = 'item'
44
43
  _MRO_ = 'MRO'
45
44
  # __DUNDER gets mangled in class
@@ -111,7 +110,7 @@ class ADict(dict):
111
110
  self._iteration = iteration
112
111
  if items:
113
112
  dict.update(self, items)
114
- return self # in RhumbLineBase.Intersecant2, _PseudoRhumbLine.Position
113
+ return self # in RhumbLineBase.Intersecant2, _PseudoRhumbLine.Position, ...
115
114
 
116
115
  def _toL(self):
117
116
  '''(INTERNAL) Get items as list.
@@ -977,8 +976,7 @@ class _NamedTuple(tuple, _Named):
977
976
  @note: Items can not be deleted.
978
977
  '''
979
978
  if name in self._Names_:
980
- t = _SPACE_(_del_, self._DOT_(name))
981
- raise _TypeError(t, txt=_immutable_)
979
+ raise _ImmutableError(self, name) # _del_
982
980
  elif name in (_name_, _name):
983
981
  _Named.__setattr__(self, name, NN) # XXX _Named.name.fset(self, NN)
984
982
  else:
@@ -1011,8 +1009,7 @@ class _NamedTuple(tuple, _Named):
1011
1009
  '''Set attribute or item B{C{name}} to B{C{value}}.
1012
1010
  '''
1013
1011
  if name in self._Names_:
1014
- t = Fmt.EQUALSPACED(self._DOT_(name), repr(value))
1015
- raise _TypeError(t, txt=_immutable_)
1012
+ raise _ImmutableError(self, name, value)
1016
1013
  elif name in (_name_, _name):
1017
1014
  _Named.__setattr__(self, name, value) # XXX _Named.name.fset(self, value)
1018
1015
  else: # e.g. _iteration
@@ -1250,7 +1247,7 @@ def modulename(clas, prefixed=None): # in .basics._xversion
1250
1247
  '''Return the class name optionally prefixed with the
1251
1248
  module name.
1252
1249
 
1253
- @arg clas: The class (any C{class}).
1250
+ @arg clas: The class (any C{class} or C{str}).
1254
1251
  @kwarg prefixed: Include the module name (C{bool}), see
1255
1252
  function C{classnaming}.
1256
1253
 
@@ -9,7 +9,7 @@ u'''Package of lazily imported C{rhumb} modules L{rhumb.aux_}, L{rhumb.ekx} and
9
9
  from pygeodesy.lazily import _ALL_LAZY, _ALL_OTHER, _lazy_import_as, _unLazy0
10
10
 
11
11
  __all__ = _ALL_LAZY.rhumb
12
- __version__ = '25.04.14'
12
+ __version__ = '25.08.28'
13
13
 
14
14
  if _unLazy0: # or _isfrozen
15
15
  from pygeodesy.rhumb.aux_ import RhumbAux, RhumbLineAux
pygeodesy/rhumb/aux_.py CHANGED
@@ -48,7 +48,7 @@ from pygeodesy.rhumb.bases import RhumbBase, RhumbLineBase, \
48
48
  from math import ceil as _ceil, fabs, radians
49
49
 
50
50
  __all__ = _ALL_LAZY.rhumb_aux_
51
- __version__ = '25.05.12'
51
+ __version__ = '25.08.31'
52
52
 
53
53
  # DIGITS = (sizeof(real) * 8) bits
54
54
  # = (ctypes.sizeof(ctypes.c_double(1.0)) * 8) bits
pygeodesy/rhumb/bases.py CHANGED
@@ -732,8 +732,7 @@ class RhumbLineBase(_CapsBase):
732
732
  azi02=other.azi12, a02=t.a12, s02=t.s12,
733
733
  at=other.azi12 - self.azi12, iteration=i)
734
734
  except Exception as x:
735
- raise IntersectionError(self, other, tol=tol,
736
- eps=eps, cause=x)
735
+ raise IntersectionError(self, other, tol=tol, cause=x, **eps)
737
736
  return P
738
737
 
739
738
  def Inverse(self, lat2, lon2, wrap=False, **outmask):
@@ -870,19 +869,19 @@ class RhumbLineBase(_CapsBase):
870
869
  P = self.Intersection(rl, tol=tol, eps=eps)
871
870
 
872
871
  else: # C{rhumb-intercept}
873
- E = self.ellipsoid
874
- _gI = E.geodesic_(exact=exact).Inverse
875
- gm = Cs.STANDARD | Cs._REDUCEDLENGTH_GEODESICSCALE # ^ Cs.DISTANCE_IN
872
+ E = self.ellipsoid
873
+ _gI = E.geodesic_(exact=exact).Inverse
874
+ gm = Cs.STANDARD | Cs._REDUCEDLENGTH_GEODESICSCALE # ^ Cs.DISTANCE_IN
875
+ _d2 = _diff182
876
876
  if est is None: # get an estimate from the "perpendicular" geodesic
877
877
  r = _gI(self.lat1, self.lon1, lat0, lon0, outmask=Cs.AZIMUTH_DISTANCE)
878
- d, _ = _diff182(r.azi2, self.azi12, K_2_0=True)
878
+ d, _ = _d2(r.azi2, self.azi12, K_2_0=True)
879
879
  _, s12 = sincos2d(d)
880
880
  s12 *= r.s12 # signed
881
881
  else:
882
882
  s12 = Meter(est=est)
883
883
  try:
884
884
  _abs = fabs
885
- _d2 = _diff182
886
885
  _ErT = E.rocPrimeVertical # aka rocTransverse
887
886
  _ovr = _over
888
887
  _S12 = Fsum(s12).fsum2f_
@@ -894,7 +893,7 @@ class RhumbLineBase(_CapsBase):
894
893
  s, c, s2, c2 = _scd(d, r.lat2)
895
894
  c2 *= _ErT(r.lat2)
896
895
  s *= _ovr(s2 * self._salp, c2) - _ovr(s * r.M21, r.m12)
897
- s12, t = _S12(c / s) # XXX _ovr?
896
+ s12, t = _S12(c / s) if s else (s12, s) # XXX _ovr?
898
897
  if _abs(t) < tol: # or _abs(c) < EPS
899
898
  break
900
899
  P.set_(azi0=r.azi1, a02=r.a12, s02=r.s12, # azi2=r.azi2,
pygeodesy/rhumb/ekx.py CHANGED
@@ -22,9 +22,9 @@ License. For more information, see the U{GeographicLib<https://GeographicLib.So
22
22
  # make sure int/int division yields float quotient
23
23
  from __future__ import division as _; del _ # noqa: E702 ;
24
24
 
25
- from pygeodesy.basics import copysign0, neg
26
- from pygeodesy.constants import PI_2, _0_0s, _0_0, _0_5, _1_0, \
27
- _2_0, _4_0, _720_0, _over, _1_over
25
+ # from pygeodesy.basics import copysign0, neg # _MODS
26
+ from pygeodesy.constants import PI_2, _over, _1_over, _0_0s, \
27
+ _0_0, _0_5, _1_0, _2_0, _4_0, _720_0
28
28
  # from pygeodesy.datums import _WGS84 # from .rhumb.bases
29
29
  # from pygeodesy.deprecated import RhumbOrder2Tuple # _MODS
30
30
  from pygeodesy.errors import RhumbError, _xkwds_pop2, _Xorder
@@ -42,7 +42,7 @@ from pygeodesy.utily import atan1, sincos2_
42
42
  from math import asinh, atan, cos, cosh, radians, sin, sinh, sqrt, tan # as _tan
43
43
 
44
44
  __all__ = _ALL_LAZY.rhumb_ekx
45
- __version__ = '25.05.12'
45
+ __version__ = '25.08.31'
46
46
 
47
47
 
48
48
  class Rhumb(RhumbBase):
@@ -159,9 +159,9 @@ class Rhumb(RhumbBase):
159
159
  if (outmask & Cs.DISTANCE):
160
160
  a = s = hypot(lon12, psi12)
161
161
  if a:
162
- a *= self._DIsometric2Rectifyingd(psi2, psi1)
163
- s = self._mpd * a # == E._Lpd
164
- a = copysign0(a, s)
162
+ a *= self._DIsometric2Rectifyingd(psi2, psi1)
163
+ s = self._mpd * a # == E._Lpd
164
+ a = _MODS.basics.copysign0(a, s)
165
165
  r.set_(a12=a, s12=s)
166
166
 
167
167
  if ((outmask | self._debug) & Cs._DEBUG_INVERSE): # PYCHOK no cover
@@ -306,7 +306,7 @@ class RhumbLine(RhumbLineBase):
306
306
 
307
307
  @Property_RO
308
308
  def _psi1(self):
309
- '''(INTERNAL) Get the I{isometric auxiliary} latitude C{psi} (C{degrees}).
309
+ '''(INTERNAL) Get the I{isometric auxiliary} latitude (C{degrees}).
310
310
  '''
311
311
  return self.ellipsoid.auxIsometric(self.lat1)
312
312
 
@@ -465,7 +465,7 @@ def _sincosSeries(sinp, x, y, C, n):
465
465
  # SC = sinp ? sin : cos
466
466
  # CS = sinp ? cos : sin
467
467
  # ...
468
- d, _neg = (x - y), neg
468
+ d, _neg = (x - y), _MODS.basics.neg
469
469
  sp, cp, sd, cd = sincos2_(x + y, d)
470
470
  sd = (sd / d) if d else _1_0
471
471
  s = _neg(sp * sd) # negative
pygeodesy/solveBase.py CHANGED
@@ -23,7 +23,7 @@ from pygeodesy.units import Precision_
23
23
  from pygeodesy.utily import unroll180
24
24
 
25
25
  __all__ = _ALL_LAZY.solveBase
26
- __version__ = '25.05.12'
26
+ __version__ = '25.09.02'
27
27
 
28
28
  _ERROR_ = 'ERROR'
29
29
 
@@ -63,10 +63,16 @@ class _SolveCapsBase(_CapsBase):
63
63
 
64
64
  @Property_RO
65
65
  def a(self):
66
- '''Get the I{equatorial} radius, semi-axis (C{meter}).
66
+ '''Get the ellipsoid's I{equatorial} radius, semi-axis (C{meter}).
67
67
  '''
68
68
  return self.ellipsoid.a
69
69
 
70
+ @Property_RO
71
+ def b(self):
72
+ '''Get the ellipsoid's I{polar} radius, semi-axis (C{meter}).
73
+ '''
74
+ return self.ellipsoid.b
75
+
70
76
  @property_RO
71
77
  def _cmdBasic(self): # PYCHOK no covers '''(INTERNAL) I{Must be overloaded}.'''
72
78
  notOverloaded(self, underOK=True)
@@ -381,6 +387,11 @@ class _SolveGDictBase(_SolveBase):
381
387
  if path:
382
388
  self._setXable(path)
383
389
 
390
+ def ArcDirect(self, lat1, lon1, azi1, a12, outmask=_UNUSED_): # PYCHOK unused
391
+ '''Return the C{Direct} result at C{a12} degrees.
392
+ '''
393
+ return self._GDictDirect(lat1, lon1, azi1, True, a12)
394
+
384
395
  @Property_RO
385
396
  def _cmdDirect(self):
386
397
  '''(INTERNAL) Get the C{Solve} I{Direct} cmd (C{tuple}).
@@ -394,7 +405,7 @@ class _SolveGDictBase(_SolveBase):
394
405
  return self._cmdBasic + ('-i',)
395
406
 
396
407
  def Direct(self, lat1, lon1, azi1, s12, outmask=_UNUSED_): # PYCHOK unused
397
- '''Return the C{Direct} result.
408
+ '''Return the C{Direct} result at distance C{s12}.
398
409
  '''
399
410
  return self._GDictDirect(lat1, lon1, azi1, False, s12)
400
411
 
@@ -17,9 +17,9 @@ U{Latitude/Longitude<https://www.Movable-Type.co.UK/scripts/latlong.html>}.
17
17
  from __future__ import division as _; del _ # noqa: E702 ;
18
18
 
19
19
  from pygeodesy.basics import copysign0, _isin, map1, signOf, typename
20
- from pygeodesy.constants import EPS, EPS1, EPS4, PI, PI2, PI_2, PI_4, R_M, \
21
- isnear0, isnear1, isnon0, _0_0, _0_5, \
22
- _1_0, _2_0, _90_0
20
+ from pygeodesy.constants import EPS, EPS1, EPS4, PI, PI2, PI_2, PI_4, \
21
+ R_M, _0_0, _0_5, _1_0, _2_0, _90_0, \
22
+ isnear0, isnear1, isnon0
23
23
  from pygeodesy.datums import _ellipsoidal_datum, _mean_radius
24
24
  from pygeodesy.errors import _AssertionError, CrossError, crosserrors, \
25
25
  _TypeError, _ValueError, IntersectionError, \
@@ -57,7 +57,7 @@ from pygeodesy.vector3d import sumOf, Vector3d
57
57
  from math import asin, cos, degrees, fabs, radians, sin
58
58
 
59
59
  __all__ = _ALL_LAZY.sphericalTrigonometry
60
- __version__ = '25.08.18'
60
+ __version__ = '25.08.31'
61
61
 
62
62
  _PI_EPS4 = PI - EPS4
63
63
  if _PI_EPS4 >= PI:
pygeodesy/streprs.py CHANGED
@@ -22,7 +22,7 @@ from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS
22
22
  from math import fabs, log10 as _log10
23
23
 
24
24
  __all__ = _ALL_LAZY.streprs
25
- __version__ = '25.04.14'
25
+ __version__ = '25.10.10'
26
26
 
27
27
  _at_ = 'at' # PYCHOK used!
28
28
  _EN_PREC = 6 # max MGRS/OSGR precision, 1 micrometer
@@ -545,22 +545,22 @@ def unstr(where, *args, **kwds_):
545
545
  @arg where: Class, function, method (C{type}) or name (C{str}).
546
546
  @arg args: Optional positional arguments.
547
547
  @kwarg kwds_: Optional keyword arguments, except C{B{_Cdot}=None},
548
- C{B{_ELLIPSIS}=False} and C{B{_fmt}=Fmt.g}.
548
+ C{B{_ELLIPSIS}=False}, C{B{_fmt}=Fmt.g} and C{B{_prec}=6}.
549
549
 
550
550
  @return: Representation (C{str}).
551
551
  '''
552
- def _C_e_g_kwds3(_Cdot=None, _ELLIPSIS=0, _fmt=Fmt.g, **kwds):
553
- return _Cdot, _ELLIPSIS, _fmt, kwds
552
+ def _C_e_fmt_prec_kwds(_Cdot=None, _ELLIPSIS=0, _fmt=Fmt.g, _prec=6, **kwds):
553
+ return _Cdot, _ELLIPSIS, dict(fmt=_fmt, prec=_prec), kwds
554
554
 
555
- C, e, g, kwds = _C_e_g_kwds3(**kwds_)
555
+ C, e, fmt_prec, kwds = _C_e_fmt_prec_kwds(**kwds_)
556
556
  if e and len(args) > (e + 1):
557
- t = reprs(args[:e], fmt=g)
557
+ t = reprs(args[:e], **fmt_prec)
558
558
  t += _ELLIPSIS_,
559
- t += reprs(args[-1:], fmt=g)
559
+ t += reprs(args[-1:], **fmt_prec)
560
560
  else:
561
- t = reprs(args, fmt=g) if args else ()
561
+ t = reprs(args, **fmt_prec) if args else ()
562
562
  if kwds:
563
- t += pairs(itemsorted(kwds), fmt=g)
563
+ t += pairs(itemsorted(kwds), **fmt_prec)
564
564
  n = where if isstr(where) else typename(where) # _NN_
565
565
  if C and hasattr(C, n):
566
566
  try: # bound method of class C?