pygeodesy 24.6.1__py2.py3-none-any.whl → 24.6.24__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 (89) hide show
  1. {PyGeodesy-24.6.1.dist-info → PyGeodesy-24.6.24.dist-info}/METADATA +2 -2
  2. PyGeodesy-24.6.24.dist-info/RECORD +117 -0
  3. pygeodesy/__init__.py +33 -32
  4. pygeodesy/albers.py +2 -2
  5. pygeodesy/auxilats/__init__.py +1 -1
  6. pygeodesy/auxilats/auxAngle.py +40 -39
  7. pygeodesy/auxilats/auxDLat.py +3 -2
  8. pygeodesy/auxilats/auxLat.py +16 -18
  9. pygeodesy/auxilats/auxily.py +1 -1
  10. pygeodesy/azimuthal.py +10 -10
  11. pygeodesy/basics.py +9 -1
  12. pygeodesy/booleans.py +53 -66
  13. pygeodesy/cartesianBase.py +143 -155
  14. pygeodesy/css.py +14 -18
  15. pygeodesy/datums.py +6 -6
  16. pygeodesy/deprecated/__init__.py +1 -1
  17. pygeodesy/deprecated/classes.py +16 -2
  18. pygeodesy/deprecated/datum.py +3 -3
  19. pygeodesy/deprecated/functions.py +6 -8
  20. pygeodesy/dms.py +23 -27
  21. pygeodesy/ecef.py +49 -55
  22. pygeodesy/elevations.py +4 -4
  23. pygeodesy/ellipsoidalBase.py +28 -70
  24. pygeodesy/ellipsoidalBaseDI.py +19 -23
  25. pygeodesy/ellipsoidalExact.py +3 -3
  26. pygeodesy/ellipsoidalGeodSolve.py +15 -23
  27. pygeodesy/ellipsoidalKarney.py +37 -60
  28. pygeodesy/ellipsoidalNvector.py +44 -50
  29. pygeodesy/ellipsoidalVincenty.py +11 -14
  30. pygeodesy/ellipsoids.py +107 -101
  31. pygeodesy/errors.py +101 -49
  32. pygeodesy/etm.py +32 -44
  33. pygeodesy/formy.py +55 -58
  34. pygeodesy/frechet.py +20 -23
  35. pygeodesy/fsums.py +4 -4
  36. pygeodesy/gars.py +3 -4
  37. pygeodesy/geodesici.py +909 -0
  38. pygeodesy/geodesicw.py +11 -13
  39. pygeodesy/geodesicx/__init__.py +4 -4
  40. pygeodesy/geodesicx/gx.py +18 -28
  41. pygeodesy/geodesicx/gxbases.py +20 -8
  42. pygeodesy/geodesicx/gxline.py +16 -22
  43. pygeodesy/geodsolve.py +102 -34
  44. pygeodesy/geohash.py +39 -60
  45. pygeodesy/geoids.py +28 -37
  46. pygeodesy/hausdorff.py +21 -23
  47. pygeodesy/heights.py +15 -28
  48. pygeodesy/internals.py +19 -12
  49. pygeodesy/interns.py +4 -10
  50. pygeodesy/iters.py +2 -2
  51. pygeodesy/karney.py +20 -4
  52. pygeodesy/ktm.py +13 -16
  53. pygeodesy/latlonBase.py +202 -191
  54. pygeodesy/lazily.py +96 -59
  55. pygeodesy/lcc.py +29 -32
  56. pygeodesy/ltp.py +43 -24
  57. pygeodesy/ltpTuples.py +190 -183
  58. pygeodesy/mgrs.py +35 -9
  59. pygeodesy/named.py +106 -72
  60. pygeodesy/namedTuples.py +43 -14
  61. pygeodesy/nvectorBase.py +23 -27
  62. pygeodesy/osgr.py +9 -9
  63. pygeodesy/points.py +7 -7
  64. pygeodesy/rhumb/__init__.py +1 -1
  65. pygeodesy/rhumb/aux_.py +5 -5
  66. pygeodesy/rhumb/bases.py +30 -31
  67. pygeodesy/rhumb/ekx.py +3 -4
  68. pygeodesy/rhumb/solve.py +8 -61
  69. pygeodesy/solveBase.py +22 -19
  70. pygeodesy/sphericalBase.py +26 -21
  71. pygeodesy/sphericalNvector.py +13 -13
  72. pygeodesy/sphericalTrigonometry.py +86 -97
  73. pygeodesy/streprs.py +8 -36
  74. pygeodesy/trf.py +3 -3
  75. pygeodesy/triaxials.py +117 -91
  76. pygeodesy/units.py +229 -321
  77. pygeodesy/unitsBase.py +116 -108
  78. pygeodesy/ups.py +26 -31
  79. pygeodesy/utily.py +12 -11
  80. pygeodesy/utm.py +35 -40
  81. pygeodesy/utmups.py +43 -46
  82. pygeodesy/utmupsBase.py +9 -10
  83. pygeodesy/vector3d.py +59 -62
  84. pygeodesy/vector3dBase.py +17 -15
  85. pygeodesy/webmercator.py +19 -21
  86. pygeodesy/wgrs.py +18 -20
  87. PyGeodesy-24.6.1.dist-info/RECORD +0 -116
  88. {PyGeodesy-24.6.1.dist-info → PyGeodesy-24.6.24.dist-info}/WHEEL +0 -0
  89. {PyGeodesy-24.6.1.dist-info → PyGeodesy-24.6.24.dist-info}/top_level.txt +0 -0
pygeodesy/mgrs.py CHANGED
@@ -55,7 +55,7 @@ from pygeodesy.utm import toUtm8, _to3zBlat, Utm, _UTM_ZONE_MAX, _UTM_ZONE_MIN
55
55
  # from pygeodesy.utmupsBase import _UTM_ZONE_MAX, _UTM_ZONE_MIN # from .utm
56
56
 
57
57
  __all__ = _ALL_LAZY.mgrs
58
- __version__ = '24.05.31'
58
+ __version__ = '24.06.11'
59
59
 
60
60
  _AN_ = 'AN' # default south pole grid tile and band B
61
61
  _AtoPx_ = _AtoZnoIO_.tillP
@@ -596,18 +596,17 @@ def toMgrs(utmups, Mgrs=Mgrs, **name_Mgrs_kwds):
596
596
  @arg utmups: A UTM or UPS coordinate (L{Utm}, L{Etm} or L{Ups}).
597
597
  @kwarg Mgrs: Optional class to return the MGRS grid reference
598
598
  (L{Mgrs}) or C{None}.
599
- @kwarg name_Mgrs_kwds: Optional C{B{name}=NN} (C{str}) and
600
- optional, additional B{C{Mgrs}} keyword arguments,
601
- ignored if C{B{Mgrs} is None}.
599
+ @kwarg name_Mgrs_kwds: Optional C{B{name}=NN} (C{str}) and optional,
600
+ additional B{C{Mgrs}} keyword arguments, ignored if
601
+ C{B{Mgrs} is None}.
602
602
 
603
- @return: The MGRS grid reference as B{C{Mgrs}} or if C{B{Mgrs}
604
- is None} as an L{Mgrs6Tuple}C{(zone, EN, easting,
605
- northing, band, datum)}.
603
+ @return: The MGRS grid reference as B{C{Mgrs}} or if C{B{Mgrs} is
604
+ None} as an L{Mgrs6Tuple}C{(zone, EN, easting, northing,
605
+ band, datum)}.
606
606
 
607
607
  @raise MGRSError: Invalid B{C{utmups}}.
608
608
 
609
- @raise TypeError: If B{C{utmups}} is not L{Utm} nor L{Etm}
610
- nor L{Ups}.
609
+ @raise TypeError: If B{C{utmups}} is not L{Utm}, L{Etm} nor L{Ups}.
611
610
  '''
612
611
  # _MODS.utmups.utmupsValidate(utmups, MGRS=True, Error-MGRSError)
613
612
  _xinstanceof(Utm, Ups, utmups=utmups) # Utm, Etm, Ups
@@ -699,6 +698,33 @@ if __name__ == '__main__':
699
698
  p = e * 100.0 / n
700
699
  printf('%6s: %s errors (%.2f%%)', n, (e if e else 'no'), p)
701
700
 
701
+ # % python3 -m pygeodesy.mgrs
702
+ # using: /opt/local/bin/GeoConvert -m ...
703
+ # 0: lat -90 ... OK
704
+ # 361: lat -89 ... OK
705
+ # 722: lat -88 ... OK
706
+ # 1083: lat -87 ... OK
707
+ # 1444: lat -86 ... OK
708
+ # 1805: lat -85 ... OK
709
+ # 2166: lat -84 ... OK
710
+ # 2527: lat -83 ... OK
711
+ # 2888: lat -82 ... OK
712
+ # 3249: lat -81 ... OK
713
+ # 3610: lat -80 ... OK
714
+ # ...
715
+ # 61370: lat 80 ... OK
716
+ # 61731: lat 81 ... OK
717
+ # 62092: lat 82 ... OK
718
+ # 62453: lat 83 ... OK
719
+ # 62814: lat 84 ... OK
720
+ # 63175: lat 85 ... OK
721
+ # 63536: lat 86 ... OK
722
+ # 63897: lat 87 ... OK
723
+ # 64258: lat 88 ... OK
724
+ # 64619: lat 89 ... OK
725
+ # 64980: lat 90 ... OK
726
+ # 65341: no errors (0.00%)
727
+
702
728
  # **) MIT License
703
729
  #
704
730
  # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
pygeodesy/named.py CHANGED
@@ -13,27 +13,28 @@ standard Python C{namedtuple}s.
13
13
  @see: Module L{pygeodesy.namedTuples} for (most of) the C{Named-Tuples}.
14
14
  '''
15
15
 
16
- from pygeodesy.basics import isclass, isidentifier, iskeyword, isstr, issubclassof, \
17
- itemsorted, len2, _xcopy, _xdup, _zip
16
+ from pygeodesy.basics import isidentifier, iskeyword, isstr, itemsorted, len2, \
17
+ _xcopy, _xdup, _xinstanceof, _xsubclassof, _zip
18
18
  from pygeodesy.errors import _AssertionError, _AttributeError, _incompatible, \
19
- _IndexError, _IsnotError, _KeyError, LenError, \
20
- _NameError, _NotImplementedError, _TypeError, \
21
- _TypesError, UnitError, _ValueError, _xattr, _xkwds, \
22
- _xkwds_item2, _xkwds_pop2
19
+ _IndexError, _KeyError, LenError, _NameError, \
20
+ _NotImplementedError, _TypeError, _TypesError, \
21
+ _UnexpectedError, UnitError, _ValueError, \
22
+ _xattr, _xkwds, _xkwds_item2, _xkwds_pop2
23
23
  from pygeodesy.internals import _caller3, _dunder_nameof, _isPyPy, _sizeof, _under
24
24
  from pygeodesy.interns import MISSING, NN, _AT_, _COLON_, _COLONSPACE_, _COMMA_, \
25
25
  _COMMASPACE_, _doesn_t_exist_, _DOT_, _DUNDER_, \
26
26
  _dunder_name_, _EQUAL_, _exists_, _immutable_, _name_, \
27
27
  _NL_, _NN_, _no_, _other_, _s_, _SPACE_, _std_, \
28
- _UNDER_, _valid_, _vs_
28
+ _UNDER_, _vs_
29
29
  from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS, _getenv
30
30
  from pygeodesy.props import _allPropertiesOf_n, deprecated_method, _hasProperty, \
31
31
  _update_all, property_doc_, Property_RO, property_RO, \
32
32
  _update_attrs
33
33
  from pygeodesy.streprs import attrs, Fmt, lrstrip, pairs, reprs, unstr
34
+ # from pygeodesy.units import _toUnit # _MODS
34
35
 
35
36
  __all__ = _ALL_LAZY.named
36
- __version__ = '24.05.31'
37
+ __version__ = '24.06.24'
37
38
 
38
39
  _COMMANL_ = _COMMA_ + _NL_
39
40
  _COMMASPACEDOT_ = _COMMASPACE_ + _DOT_
@@ -71,6 +72,15 @@ class ADict(dict):
71
72
  '''
72
73
  return self.toRepr()
73
74
 
75
+ def __setattr__(self, name, value):
76
+ '''Set the value of a I{known} item by B{C{name}}.
77
+ '''
78
+ try:
79
+ if self[name] != value:
80
+ self[name] = value
81
+ except KeyError:
82
+ dict.__setattr__(self, name, value)
83
+
74
84
  def __str__(self):
75
85
  '''Default C{str(self)}.
76
86
  '''
@@ -240,13 +250,14 @@ class _Named(object):
240
250
  # _update_all(d)
241
251
  return d
242
252
 
243
- def _instr(self, name, prec, *attrs, **fmt_props_kwds):
244
- '''(INTERNAL) Format, used by C{Conic}, C{Ellipsoid}, C{Transform}, C{Triaxial}.
253
+ def _instr(self, *attrs, **fmt_prec_props_sep_name__kwds):
254
+ '''(INTERNAL) Format, used by C{Conic}, C{Ellipsoid}, C{Geodesic...}, C{Transform}, C{Triaxial}.
245
255
  '''
246
- def _fmt_props_kwds(fmt=Fmt.F, props=(), **kwds):
247
- return fmt, props, kwds
256
+ def _fmt_prec_props_kwds(fmt=Fmt.F, prec=6, props=(), sep=_COMMASPACE_, **kwds):
257
+ return fmt, prec, props, sep, kwds
248
258
 
249
- fmt, props, kwds = _fmt_props_kwds(**fmt_props_kwds)
259
+ name, kwds = _name2__(**fmt_prec_props_sep_name__kwds)
260
+ fmt, prec, props, sep, kwds = _fmt_prec_props_kwds(**kwds)
250
261
 
251
262
  t = () if name is None else (Fmt.EQUAL(name=repr(name or self.name)),)
252
263
  if attrs:
@@ -257,7 +268,7 @@ class _Named(object):
257
268
  prec=prec, ints=True)
258
269
  if kwds:
259
270
  t += pairs(kwds, prec=prec)
260
- return _COMMASPACE_.join(t)
271
+ return sep.join(t) if sep else t
261
272
 
262
273
  @property_RO
263
274
  def iteration(self): # see .karney.GDict
@@ -309,6 +320,11 @@ class _Named(object):
309
320
  '''
310
321
  return _name__(name, _or_nameof=self) # nameof(self)
311
322
 
323
+ def _name1__(self, kwds):
324
+ '''(INTERNAL) Resolve and set the C{B{name}=NN}.
325
+ '''
326
+ return _name1__(kwds, _or_nameof=self.name) if self.name else kwds
327
+
312
328
  @Property_RO
313
329
  def named(self):
314
330
  '''Get the name I{or} class name or C{""} (C{str}).
@@ -422,11 +438,8 @@ class _Named(object):
422
438
 
423
439
  @raise TypeError: Not C{isinstance(B{inst}, _Named)}.
424
440
  '''
425
- if not isinstance(inst, _Named):
426
- raise _IsnotError(_valid_, inst=inst)
427
-
428
- _ = inst.rename(self.name)
429
- return inst
441
+ _xinstanceof(_Named, inst=inst) # assert
442
+ return inst.renamed(self.name)
430
443
 
431
444
  _Named_Property_ROs = _allPropertiesOf_n(5, _Named, Property_RO) # PYCHOK once
432
445
 
@@ -716,12 +729,14 @@ class _NamedEnum(_NamedDict):
716
729
  if not (n and isstr(n) and isidentifier(n)):
717
730
  raise ValueError()
718
731
  except (AttributeError, ValueError, TypeError) as x:
719
- raise _NameError(_DOT_(_item_, _name_), item, cause=x)
732
+ n = _DOT_(_item_, _name_)
733
+ raise _NameError(n, item, cause=x)
720
734
  if n in self:
721
735
  t = _SPACE_(_item_, self._DOT_(n), _exists_)
722
736
  raise _NameError(t, txt=repr(item))
723
- if not isinstance(item, self._item_Classes):
724
- raise _TypesError(self._DOT_(n), item, *self._item_Classes)
737
+ if not isinstance(item, self._item_Classes): # _xinstanceof
738
+ n = self._DOT_(n)
739
+ raise _TypesError(n, item, *self._item_Classes)
725
740
  self[n] = item
726
741
  return n
727
742
 
@@ -843,7 +858,7 @@ class _NamedEnumItem(_NamedBase):
843
858
  '''
844
859
  name = _name__(name) or _NN_
845
860
  if self._enum:
846
- raise _NameError(name, self, txt=_registered_) # XXX _TypeError
861
+ raise _NameError(name, self, txt=_registered_) # _TypeError
847
862
  if name:
848
863
  self._name = name
849
864
 
@@ -870,8 +885,8 @@ class _NamedEnumItem(_NamedBase):
870
885
  enum = self._enum
871
886
  if enum and self.name and self.name in enum:
872
887
  item = enum.unregister(self.name)
873
- if item is not self:
874
- t = _SPACE_(repr(item), _vs_, repr(self)) # PYCHOK no cover
888
+ if item is not self: # PYCHOK no cover
889
+ t = _SPACE_(repr(item), _vs_, repr(self))
875
890
  raise _AssertionError(t)
876
891
 
877
892
 
@@ -923,7 +938,11 @@ class _NamedTuple(tuple, _Named):
923
938
  raise LenError(self.__class__, args=n, _Names_=N)
924
939
 
925
940
  if iteration_name:
926
- self._kwdself(**iteration_name)
941
+ i, name = _xkwds_pop2(iteration_name, iteration=None)
942
+ if i is not None:
943
+ self._iteration = i
944
+ if name:
945
+ self.name = name
927
946
  return self
928
947
 
929
948
  def __delattr__(self, name):
@@ -932,7 +951,8 @@ class _NamedTuple(tuple, _Named):
932
951
  @note: Items can not be deleted.
933
952
  '''
934
953
  if name in self._Names_:
935
- raise _TypeError(_del_, _DOT_(self.classname, name), txt=_immutable_)
954
+ t = _SPACE_(_del_, self._DOT_(name))
955
+ raise _TypeError(t, txt=_immutable_)
936
956
  elif name in (_name_, _name):
937
957
  _Named.__setattr__(self, name, NN) # XXX _Named.name.fset(self, NN)
938
958
  else:
@@ -943,10 +963,10 @@ class _NamedTuple(tuple, _Named):
943
963
  '''
944
964
  try:
945
965
  return tuple.__getitem__(self, self._Names_.index(name))
946
- except IndexError:
947
- raise _IndexError(_DOT_(self.classname, Fmt.ANGLE(_name_)), name)
966
+ except IndexError as x:
967
+ raise _IndexError(self._DOT_(name), cause=x)
948
968
  except ValueError: # e.g. _iteration
949
- return tuple.__getattribute__(self, name)
969
+ return tuple.__getattr__(self, name) # __getattribute__
950
970
 
951
971
  # def __getitem__(self, index): # index, slice, etc.
952
972
  # '''Get the item(s) at an B{C{index}} or slice.
@@ -965,7 +985,8 @@ class _NamedTuple(tuple, _Named):
965
985
  '''Set attribute or item B{C{name}} to B{C{value}}.
966
986
  '''
967
987
  if name in self._Names_:
968
- raise _TypeError(_DOT_(self.classname, name), value, txt=_immutable_)
988
+ t = Fmt.EQUALSPACED(self._DOT_(name), repr(value))
989
+ raise _TypeError(t, txt=_immutable_)
969
990
  elif name in (_name_, _name):
970
991
  _Named.__setattr__(self, name, value) # XXX _Named.name.fset(self, value)
971
992
  else: # e.g. _iteration
@@ -976,6 +997,11 @@ class _NamedTuple(tuple, _Named):
976
997
  '''
977
998
  return self.toStr()
978
999
 
1000
+ def _DOT_(self, *names):
1001
+ '''(INTERNAL) Period-join C{self.classname} and C{names}.
1002
+ '''
1003
+ return _DOT_(self.classname, *names)
1004
+
979
1005
  def dup(self, name=NN, **items):
980
1006
  '''Duplicate this tuple replacing one or more items.
981
1007
 
@@ -986,15 +1012,18 @@ class _NamedTuple(tuple, _Named):
986
1012
 
987
1013
  @raise NameError: Some B{C{items}} invalid.
988
1014
  '''
989
- tl = list(self)
1015
+ t = list(self)
1016
+ U = self._Units_
990
1017
  if items:
991
- _ix = self._Names_.index
1018
+ _ix = self._Names_.index
1019
+ _2U = _MODS.units._toUnit
992
1020
  try:
993
1021
  for n, v in items.items():
994
- tl[_ix(n)] = v
1022
+ i = _ix(n)
1023
+ t[i] = _2U(U[i], v, name=n)
995
1024
  except ValueError: # bad item name
996
- raise _NameError(_DOT_(self.classname, n), v, this=self)
997
- return self.classof(*tl, name=name or self.name)
1025
+ raise _NameError(self._DOT_(n), v, this=self)
1026
+ return self.classof(*t).reUnit(*U, name=name)
998
1027
 
999
1028
  def items(self):
1000
1029
  '''Yield the items, each as a C{(name, value)} pair (C{2-tuple}).
@@ -1006,13 +1035,23 @@ class _NamedTuple(tuple, _Named):
1006
1035
 
1007
1036
  iteritems = items
1008
1037
 
1009
- def _kwdself(self, iteration=None, **name):
1010
- '''(INTERNAL) Set C{__new__} keyword arguments.
1038
+ def reUnit(self, *Units, **name):
1039
+ '''Replace some of this C{Named-Tuple}'s C{Units}.
1040
+
1041
+ @arg Units: One or more C{Unit} classes, all positional.
1042
+ @kwarg name: Optional C{B{name}=NN} (C{str}).
1043
+
1044
+ @return: This instance with updated C{Units}.
1045
+
1046
+ @note: This C{Named-Tuple}'s values are I{not updated}.
1011
1047
  '''
1012
- if iteration is not None:
1013
- self._iteration = iteration
1014
- if name:
1015
- self.name = name
1048
+ U = self._Units_
1049
+ n = min(len(U), len(Units))
1050
+ if n:
1051
+ R = Units + U[n:]
1052
+ if R != U:
1053
+ self._Units_ = R
1054
+ return self.renamed(name) if name else self
1016
1055
 
1017
1056
  def toRepr(self, prec=6, sep=_COMMASPACE_, fmt=Fmt.F, **unused): # PYCHOK signature
1018
1057
  '''Return this C{Named-Tuple} items as C{name=value} string(s).
@@ -1043,37 +1082,33 @@ class _NamedTuple(tuple, _Named):
1043
1082
  '''
1044
1083
  return Fmt.PAREN(sep.join(reprs(self, prec=prec, fmt=fmt)))
1045
1084
 
1046
- def toUnits(self, Error=UnitError): # overloaded in .frechet, .hausdorff
1085
+ def toUnits(self, Error=UnitError, **name): # overloaded in .frechet, .hausdorff
1047
1086
  '''Return a copy of this C{Named-Tuple} with each item value wrapped
1048
1087
  as an instance of its L{units} class.
1049
1088
 
1050
1089
  @kwarg Error: Error to raise for L{units} issues (C{UnitError}).
1090
+ @kwarg name: Optional C{B{name}=NN} (C{str}).
1051
1091
 
1052
1092
  @return: A duplicate of this C{Named-Tuple} (C{C{Named-Tuple}}).
1053
1093
 
1054
1094
  @raise Error: Invalid C{Named-Tuple} item or L{units} class.
1055
1095
  '''
1056
- t = (v for _, v in self.units(Error=Error))
1057
- return self.classof(*tuple(t))
1096
+ t = tuple(v for _, v in self.units(Error=Error))
1097
+ return self.classof(*t).reUnit(*self._Units_, **name)
1058
1098
 
1059
- def units(self, Error=UnitError):
1060
- '''Yield the items, each as a C{(name, value}) pair (C{2-tuple}) with
1061
- the value wrapped as an instance of its L{units} class.
1099
+ def units(self, **Error):
1100
+ '''Yield the items, each as a C{2-tuple (name, value}) with the
1101
+ value wrapped as an instance of its L{units} class.
1062
1102
 
1063
- @kwarg Error: Error to raise for L{units} issues (C{UnitError}).
1103
+ @kwarg Error: Optional C{B{Error}=UnitError} to raise.
1064
1104
 
1065
1105
  @raise Error: Invalid C{Named-Tuple} item or L{units} class.
1066
1106
 
1067
1107
  @see: Method C{.items}.
1068
1108
  '''
1109
+ _2U = _MODS.units._toUnit
1069
1110
  for n, v, U in _zip(self._Names_, self, self._Units_): # strict=True
1070
- if not (v is None or U is None
1071
- or (isclass(U) and
1072
- isinstance(v, U) and
1073
- hasattr(v, _name_) and
1074
- v.name == n)): # PYCHOK indent
1075
- v = U(v, name=n, Error=Error)
1076
- yield n, v
1111
+ yield n, _2U(U, v, name=n, **Error)
1077
1112
 
1078
1113
  iterunits = units
1079
1114
 
@@ -1083,35 +1118,35 @@ class _NamedTuple(tuple, _Named):
1083
1118
  '''
1084
1119
  ns = self._Names_
1085
1120
  if not (isinstance(ns, tuple) and len(ns) > 1): # XXX > 0
1086
- raise _TypeError(_DOT_(self.classname, _Names_), ns)
1121
+ raise _TypeError(self._DOT_(_Names_), ns)
1087
1122
  for i, n in enumerate(ns):
1088
1123
  if not _xvalid(n, underOK=underOK):
1089
1124
  t = Fmt.SQUARE(_Names_=i) # PYCHOK no cover
1090
- raise _ValueError(_DOT_(self.classname, t), n)
1125
+ raise _ValueError(self._DOT_(t), n)
1091
1126
 
1092
1127
  us = self._Units_
1093
1128
  if not isinstance(us, tuple):
1094
- raise _TypeError(_DOT_(self.classname, _Units_), us)
1129
+ raise _TypeError(self._DOT_(_Units_), us)
1095
1130
  if len(us) != len(ns):
1096
1131
  raise LenError(self.__class__, _Units_=len(us), _Names_=len(ns))
1097
1132
  for i, u in enumerate(us):
1098
1133
  if not (u is None or callable(u)):
1099
1134
  t = Fmt.SQUARE(_Units_=i) # PYCHOK no cover
1100
- raise _TypeError(_DOT_(self.classname, t), u)
1135
+ raise _TypeError(self._DOT_(t), u)
1101
1136
 
1102
1137
  self.__class__._validated = True
1103
1138
 
1104
1139
  def _xtend(self, xTuple, *items, **name):
1105
1140
  '''(INTERNAL) Extend this C{Named-Tuple} with C{items} to an other B{C{xTuple}}.
1106
1141
  '''
1107
- if (issubclassof(xTuple, _NamedTuple) and
1108
- (len(self._Names_) + len(items)) == len(xTuple._Names_) and
1109
- self._Names_ == xTuple._Names_[:len(self)]):
1110
- n = _name__(**name) or self.name
1111
- return xTuple(self + items, name=n) # *(self + items)
1112
- c = NN(self.classname, repr(self._Names_)) # PYCHOK no cover
1113
- x = NN(xTuple.__name__, repr(xTuple._Names_)) # PYCHOK no cover
1114
- raise TypeError(_SPACE_(c, _vs_, x))
1142
+ _xsubclassof(_NamedTuple, xTuple=xTuple)
1143
+ if len(xTuple._Names_) != (len(self._Names_) + len(items)) or \
1144
+ xTuple._Names_[:len(self)] != self._Names_: # PYCHOK no cover
1145
+ c = NN(self.classname, repr(self._Names_))
1146
+ x = NN(xTuple.__name__, repr(xTuple._Names_))
1147
+ raise TypeError(_SPACE_(c, _vs_, x))
1148
+ t = self + items
1149
+ return xTuple(t, name=self._name__(name)) # .reUnit(*self._Units_)
1115
1150
 
1116
1151
 
1117
1152
  def callername(up=1, dflt=NN, source=False, underOK=False):
@@ -1231,8 +1266,7 @@ def _name__(name=NN, **kwds):
1231
1266
  if name or kwds:
1232
1267
  name, kwds = _name2__(name, **kwds)
1233
1268
  if kwds: # "unexpected keyword arguments ..."
1234
- m = _MODS.errors
1235
- raise m._UnexpectedError(**kwds)
1269
+ raise _UnexpectedError(**kwds)
1236
1270
  return name if name or name is None else NN
1237
1271
 
1238
1272
 
@@ -1255,11 +1289,11 @@ def _name2__(name=NN, name__=None, _or_nameof=None, **kwds):
1255
1289
  else:
1256
1290
  n = str(name)
1257
1291
  elif name__ is not None:
1258
- n = getattr(name__, _dunder_name_, NN) # _xattr(name__, __name__=NN)
1292
+ n = _dunder_nameof(name__, NN)
1259
1293
  else:
1260
1294
  n = name if name is None else NN
1261
1295
  if _or_nameof is not None and not n:
1262
- n = getattr(_or_nameof, _name_, NN) # _xattr(_or_nameof, name=NN)
1296
+ n = _xattr(_or_nameof, name=NN) # nameof
1263
1297
  return n, kwds # (str or None or {}), dict
1264
1298
 
1265
1299
 
pygeodesy/namedTuples.py CHANGED
@@ -8,26 +8,27 @@ are all instances of some C{Named...Tuple} class, all sub-classes
8
8
  of C{_NamedTuple} defined in C{pygeodesy.named}.
9
9
  '''
10
10
 
11
- from pygeodesy.basics import map1, _xinstanceof
11
+ from pygeodesy.basics import isinstanceof, map1, _xinstanceof
12
12
  # from pygeodesy.constants import INT0 # from .units
13
- from pygeodesy.errors import _ALL_LAZY, _MODS, _xattr, _xkwds_not # _xkwds
14
- from pygeodesy.interns import _1_, _2_, _a_, _A_, _area_, _angle_, _b_, _B_, \
15
- _band_, _c_, _C_, _datum_, _D_, _distance_, \
16
- _E_, _easting_, _end_, _fi_, _gamma_, _height_, \
17
- _h_, _j_, _hemipole_, _initial_, _lam_, _lat_, \
18
- _lon_, _n_, _northing_, _number_, _outside_, \
19
- _phi_, _point_, _precision_, _points_, _radius_, \
20
- _scale_, _start_, _x_, _y_, _z_, _zone_
13
+ # from pygeodesy.dms import toDMS # _MODS
14
+ from pygeodesy.errors import _xattr, _xkwds, _xkwds_not, _ALL_LAZY, _MODS
15
+ from pygeodesy.interns import NN, _1_, _2_, _a_, _A_, _area_, _angle_, _b_, _B_, \
16
+ _band_, _c_, _C_, _D_, _datum_, _distance_, _E_, \
17
+ _easting_, _end_, _fi_, _gamma_, _h_, _height_, \
18
+ _hemipole_, _initial_, _j_, _lam_, _lat_, _lon_, \
19
+ _n_, _northing_, _number_, _outside_, _phi_, \
20
+ _point_, _precision_, _points_, _radius_, _scale_, \
21
+ _start_, _x_, _y_, _z_, _zone_
21
22
  # from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS # from .errors
22
23
  from pygeodesy.named import _NamedTuple, _Pass
23
24
  from pygeodesy.props import deprecated_property_RO, property_RO
24
- from pygeodesy.units import Band, Bearing, Degrees, Degrees2, Easting, \
25
- FIx, Height, Int, INT0, Lam, Lat, Lon, Meter, \
26
- Meter2, Northing, Number_, Phi, Precision_, \
27
- Radians, Radius, Scalar, Str
25
+ from pygeodesy.units import Band, Bearing, Degrees, Degrees2, Easting, FIx, \
26
+ Height, Int, Lam, Lat, Lon, Meter, Meter2, \
27
+ Northing, Number_, Phi, Precision_, Radians, \
28
+ Radius, Scalar, Str, INT0
28
29
 
29
30
  __all__ = _ALL_LAZY.namedTuples
30
- __version__ = '24.05.18'
31
+ __version__ = '24.06.08'
31
32
 
32
33
  # __DUNDER gets mangled in class
33
34
  _closest_ = 'closest'
@@ -324,6 +325,34 @@ class LatLonPrec5Tuple(LatLonPrec3Tuple): # .wgrs.py
324
325
  _Units_ = LatLonPrec3Tuple._Units_ + ( Height, Radius)
325
326
 
326
327
 
328
+ class _NamedTupleTo(_NamedTuple): # in .testNamedTuples
329
+ '''(INTERNAL) Base for C{-.toDegrees}, C{-.toRadians}.
330
+ '''
331
+ def _Degrees3(self, *xs, **toDMS_kwds):
332
+ '''(INTERNAL) Convert C{xs} from C{Radians} to C{Degrees} or C{toDMS}.
333
+ '''
334
+ if toDMS_kwds:
335
+ toDMS_kwds = _xkwds(toDMS_kwds, ddd=1, pos=NN)
336
+ toDMS, s = _MODS.dms.toDMS, None
337
+ else:
338
+ toDMS, s = None, self
339
+ for x in xs:
340
+ if not isinstanceof(x, Degrees):
341
+ x, s = x.toDegrees(), None
342
+ yield toDMS(x, **toDMS_kwds) if toDMS else x
343
+ yield s
344
+
345
+ def _Radians3(self, *xs, **unused):
346
+ '''(INTERNAL) Convert C{xs} from C{Degrees} to C{Radians}.
347
+ '''
348
+ s = self
349
+ for x in xs:
350
+ if not isinstanceof(x, Radians):
351
+ x, s = x.toRadians(), None
352
+ yield x
353
+ yield s
354
+
355
+
327
356
  class NearestOn2Tuple(_NamedTuple): # .ellipsoidalBaseDI
328
357
  '''2-Tuple C{(closest, fraction)} of the C{closest} point
329
358
  on and C{fraction} along a line (segment) between two
pygeodesy/nvectorBase.py CHANGED
@@ -14,7 +14,7 @@ and published under the same MIT Licence**, see U{Vector-based geodesy
14
14
  from pygeodesy.constants import EPS, EPS1, EPS_2, R_M, _2_0, _N_2_0
15
15
  # from pygeodesy.datums import _spherical_datum # from .formy
16
16
  from pygeodesy.errors import IntersectionError, _ValueError, VectorError, \
17
- _xkwds, _xkwds_pop2
17
+ _xattrs, _xkwds, _xkwds_pop2
18
18
  from pygeodesy.fmath import fdot, fidw, hypot_ # PYCHOK fdot shared
19
19
  from pygeodesy.fsums import Fsum, fsumf_
20
20
  from pygeodesy.formy import _isequalTo, n_xyz2latlon, n_xyz2philam, \
@@ -22,8 +22,7 @@ from pygeodesy.formy import _isequalTo, n_xyz2latlon, n_xyz2philam, \
22
22
  # from pygeodesy.internals import _under # from .named
23
23
  from pygeodesy.interns import NN, _1_, _2_, _3_, _bearing_, _coincident_, \
24
24
  _COMMASPACE_, _distance_, _h_, _insufficient_, \
25
- _intersection_, _no_, _NorthPole_, _point_, \
26
- _pole_, _SPACE_, _SouthPole_
25
+ _intersection_, _no_, _point_, _pole_, _SPACE_
27
26
  from pygeodesy.latlonBase import LatLonBase, _ALL_DOCS, _ALL_LAZY, _MODS
28
27
  # from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS # from .latlonBase
29
28
  from pygeodesy.named import _xother3, _under
@@ -31,15 +30,15 @@ from pygeodesy.namedTuples import Trilaterate5Tuple, Vector3Tuple, \
31
30
  Vector4Tuple, map1
32
31
  from pygeodesy.props import deprecated_method, Property_RO, property_doc_, \
33
32
  property_RO, _update_all
34
- from pygeodesy.streprs import Fmt, hstr, unstr, _xattrs
33
+ from pygeodesy.streprs import Fmt, hstr, unstr
35
34
  from pygeodesy.units import Bearing, Height, Radius_, Scalar
36
35
  from pygeodesy.utily import sincos2d, _unrollon, _unrollon3
37
- from pygeodesy.vector3d import Vector3d, _xyzhdn3
36
+ from pygeodesy.vector3d import Vector3d, _xyzhdlln4
38
37
 
39
38
  from math import fabs, sqrt
40
39
 
41
40
  __all__ = _ALL_LAZY.nvectorBase
42
- __version__ = '24.05.31'
41
+ __version__ = '24.06.12'
43
42
 
44
43
 
45
44
  class NvectorBase(Vector3d): # XXX kept private
@@ -49,32 +48,29 @@ class NvectorBase(Vector3d): # XXX kept private
49
48
  _h = Height(h=0) # height (C{meter})
50
49
  _H = NN # height prefix (C{str}), '↑' in JS version
51
50
 
52
- def __init__(self, x_xyz, y=None, z=None, h=0, ll=None, datum=None, **name):
51
+ def __init__(self, x_xyz, y=None, z=None, h=0, datum=None, **ll_name):
53
52
  '''New n-vector normal to the earth's surface.
54
53
 
55
54
  @arg x_xyz: X component of vector (C{scalar}) or (3-D) vector
56
- (C{Nvector}, L{Vector3d}, L{Vector3Tuple} or
57
- L{Vector4Tuple}).
58
- @kwarg y: Y component of vector (C{scalar}), ignored if B{C{x_xyz}}
59
- is not C{scalar}, otherwise same units as B{C{x_xyz}}.
60
- @kwarg z: Z component of vector (C{scalar}), ignored if B{C{x_xyz}}
61
- is not C{scalar}, otherwise same units as B{C{x_xyz}}.
55
+ (C{Nvector}, L{Vector3d}, L{Vector3Tuple} or L{Vector4Tuple}).
56
+ @kwarg y: Y component of vector (C{scalar}), required if B{C{x_xyz}} is
57
+ C{scalar} and same units as B{C{x_xyz}}, ignored otherwise.
58
+ @kwarg z: Z component of vector (C{scalar}), like B{C{y}}.
62
59
  @kwarg h: Optional height above surface (C{meter}).
63
- @kwarg ll: Optional, original latlon (C{LatLon}).
64
60
  @kwarg datum: Optional, I{pass-thru} datum (L{Datum}).
65
- @kwarg name: Optional C{B{name}=NN} (C{str}).
61
+ @kwarg ll_name: Optional C{B{name}=NN} (C{str}) and optional, original
62
+ latlon C{B{ll}=None} (C{LatLon}).
66
63
 
67
- @raise TypeError: Non-scalar B{C{x}}, B{C{y}} or B{C{z}}
68
- coordinate or B{C{x}} not an C{Nvector},
69
- L{Vector3Tuple} or L{Vector4Tuple} or
70
- invalid B{C{datum}}.
64
+ @raise TypeError: Non-scalar B{C{x}}, B{C{y}} or B{C{z}} coordinate or
65
+ B{C{x_xyz}} not an C{Nvector}, L{Vector3Tuple} or
66
+ L{Vector4Tuple} or invalid B{C{datum}}.
71
67
  '''
72
- h, d, n = _xyzhdn3(x_xyz, h, datum, ll, **name)
68
+ h, d, ll, n = _xyzhdlln4(x_xyz, h, datum, **ll_name)
73
69
  Vector3d.__init__(self, x_xyz, y=y, z=z, ll=ll, name=n)
74
70
  if h:
75
71
  self.h = h
76
72
  if d is not None:
77
- self._datum = _spherical_datum(d, name=self.name) # pass-thru
73
+ self._datum = _spherical_datum(d, name=n) # pass-thru
78
74
 
79
75
  @Property_RO
80
76
  def datum(self):
@@ -326,7 +322,7 @@ class NvectorBase(Vector3d): # XXX kept private
326
322
  r = self.toCartesian(h=h, Cartesian=None, datum=d)
327
323
  else:
328
324
  kwds = _xkwds(LatLon_kwds, height=h, datum=d)
329
- r = self._xnamed(LatLon(self.lat, self.lon, **kwds))
325
+ r = LatLon(self.lat, self.lon, **self._name1__(kwds))
330
326
  return r
331
327
 
332
328
  def toStr(self, prec=5, fmt=Fmt.PAREN, sep=_COMMASPACE_): # PYCHOK expected
@@ -349,7 +345,7 @@ class NvectorBase(Vector3d): # XXX kept private
349
345
  def toVector3d(self, norm=True):
350
346
  '''Convert this n-vector to a 3-D vector, I{ignoring height}.
351
347
 
352
- @kwarg norm: Normalize the 3-D vector (C{bool}).
348
+ @kwarg norm: If C{True}, normalize the 3-D vector (C{bool}).
353
349
 
354
350
  @return: The (normalized) vector (L{Vector3d}).
355
351
  '''
@@ -369,7 +365,7 @@ class NvectorBase(Vector3d): # XXX kept private
369
365
 
370
366
  @return: Normalized vector (C{Nvector}).
371
367
  '''
372
- return _xattrs(Vector3d.unit(self, ll=ll), _under(_h_))
368
+ return _xattrs(Vector3d.unit(self, ll=ll), self, _under(_h_))
373
369
 
374
370
  @Property_RO
375
371
  def xyzh(self):
@@ -378,8 +374,8 @@ class NvectorBase(Vector3d): # XXX kept private
378
374
  return self.xyz.to4Tuple(self.h)
379
375
 
380
376
 
381
- NorthPole = NvectorBase(0, 0, +1, name=_NorthPole_) # North pole (C{Nvector})
382
- SouthPole = NvectorBase(0, 0, -1, name=_SouthPole_) # South pole (C{Nvector})
377
+ NorthPole = NvectorBase(0, 0, +1, name='NorthPole') # North pole (C{Nvector})
378
+ SouthPole = NvectorBase(0, 0, -1, name='SouthPole') # South pole (C{Nvector})
383
379
 
384
380
 
385
381
  class _N_vector_(NvectorBase):
@@ -575,7 +571,7 @@ def sumOf(nvectors, Vector=None, h=None, **Vector_kwds):
575
571
  arguments, ignored if C{B{Vector} is None}.
576
572
 
577
573
  @return: Vectorial sum (B{C{Vector}}) or a L{Vector4Tuple}C{(x, y,
578
- z, h)} if B{C{Vector}} is C{None}.
574
+ z, h)} if C{B{Vector} is None}.
579
575
 
580
576
  @raise VectorError: No B{C{nvectors}}.
581
577
  '''