pygeodesy 25.11.5__py2.py3-none-any.whl → 25.12.12__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 (125) hide show
  1. pygeodesy/__init__.py +25 -12
  2. pygeodesy/__main__.py +1 -1
  3. pygeodesy/albers.py +1 -1
  4. pygeodesy/angles.py +960 -0
  5. pygeodesy/auxilats/_CX_4.py +1 -1
  6. pygeodesy/auxilats/_CX_6.py +1 -1
  7. pygeodesy/auxilats/_CX_8.py +1 -1
  8. pygeodesy/auxilats/_CX_Rs.py +1 -1
  9. pygeodesy/auxilats/__init__.py +2 -2
  10. pygeodesy/auxilats/__main__.py +1 -1
  11. pygeodesy/auxilats/auxAngle.py +7 -8
  12. pygeodesy/auxilats/auxDLat.py +1 -1
  13. pygeodesy/auxilats/auxDST.py +1 -1
  14. pygeodesy/auxilats/auxLat.py +1 -1
  15. pygeodesy/auxilats/auxily.py +1 -1
  16. pygeodesy/azimuthal.py +6 -5
  17. pygeodesy/basics.py +14 -10
  18. pygeodesy/booleans.py +1 -1
  19. pygeodesy/cartesianBase.py +7 -7
  20. pygeodesy/clipy.py +1 -1
  21. pygeodesy/constants.py +27 -24
  22. pygeodesy/css.py +1 -1
  23. pygeodesy/datums.py +1 -1
  24. pygeodesy/deprecated/__init__.py +1 -1
  25. pygeodesy/deprecated/bases.py +1 -1
  26. pygeodesy/deprecated/classes.py +14 -7
  27. pygeodesy/deprecated/consterns.py +1 -1
  28. pygeodesy/deprecated/datum.py +1 -1
  29. pygeodesy/deprecated/functions.py +1 -1
  30. pygeodesy/deprecated/nvector.py +1 -1
  31. pygeodesy/deprecated/rhumbBase.py +1 -1
  32. pygeodesy/deprecated/rhumbaux.py +1 -1
  33. pygeodesy/deprecated/rhumbsolve.py +1 -1
  34. pygeodesy/deprecated/rhumbx.py +1 -1
  35. pygeodesy/dms.py +1 -1
  36. pygeodesy/ecef.py +1 -1
  37. pygeodesy/ecefLocals.py +1 -1
  38. pygeodesy/elevations.py +1 -1
  39. pygeodesy/ellipsoidalBase.py +1 -1
  40. pygeodesy/ellipsoidalBaseDI.py +1 -1
  41. pygeodesy/ellipsoidalExact.py +1 -1
  42. pygeodesy/ellipsoidalGeodSolve.py +1 -1
  43. pygeodesy/ellipsoidalKarney.py +1 -1
  44. pygeodesy/ellipsoidalNvector.py +1 -1
  45. pygeodesy/ellipsoidalVincenty.py +1 -1
  46. pygeodesy/ellipsoids.py +7 -6
  47. pygeodesy/elliptic.py +1 -1
  48. pygeodesy/epsg.py +1 -1
  49. pygeodesy/errors.py +8 -4
  50. pygeodesy/etm.py +1 -1
  51. pygeodesy/fmath.py +15 -8
  52. pygeodesy/formy.py +107 -5
  53. pygeodesy/frechet.py +1 -1
  54. pygeodesy/fstats.py +1 -1
  55. pygeodesy/fsums.py +1 -1
  56. pygeodesy/gars.py +1 -1
  57. pygeodesy/geod3solve.py +488 -0
  58. pygeodesy/geodesici.py +4 -4
  59. pygeodesy/geodesicw.py +1 -1
  60. pygeodesy/geodesicx/_C4_24.py +1 -1
  61. pygeodesy/geodesicx/_C4_27.py +1 -1
  62. pygeodesy/geodesicx/_C4_30.py +1 -1
  63. pygeodesy/geodesicx/__init__.py +1 -1
  64. pygeodesy/geodesicx/__main__.py +1 -1
  65. pygeodesy/geodesicx/gx.py +1 -1
  66. pygeodesy/geodesicx/gxarea.py +1 -1
  67. pygeodesy/geodesicx/gxbases.py +1 -1
  68. pygeodesy/geodesicx/gxline.py +1 -1
  69. pygeodesy/geodsolve.py +70 -102
  70. pygeodesy/geohash.py +1 -1
  71. pygeodesy/geoids.py +1 -1
  72. pygeodesy/hausdorff.py +1 -1
  73. pygeodesy/heights.py +1 -1
  74. pygeodesy/internals.py +1 -1
  75. pygeodesy/interns.py +3 -3
  76. pygeodesy/iters.py +1 -1
  77. pygeodesy/karney.py +132 -116
  78. pygeodesy/ktm.py +1 -1
  79. pygeodesy/latlonBase.py +1 -1
  80. pygeodesy/lazily.py +23 -12
  81. pygeodesy/lcc.py +1 -1
  82. pygeodesy/ltp.py +1 -1
  83. pygeodesy/ltpTuples.py +1 -1
  84. pygeodesy/mgrs.py +3 -3
  85. pygeodesy/named.py +14 -9
  86. pygeodesy/namedTuples.py +1 -1
  87. pygeodesy/nvectorBase.py +1 -1
  88. pygeodesy/osgr.py +1 -1
  89. pygeodesy/points.py +1 -1
  90. pygeodesy/props.py +1 -1
  91. pygeodesy/resections.py +1 -1
  92. pygeodesy/rhumb/__init__.py +8 -6
  93. pygeodesy/rhumb/aux_.py +1 -1
  94. pygeodesy/rhumb/bases.py +1 -1
  95. pygeodesy/rhumb/ekx.py +1 -1
  96. pygeodesy/rhumb/solve.py +91 -84
  97. pygeodesy/simplify.py +1 -1
  98. pygeodesy/solveBase.py +72 -49
  99. pygeodesy/sphericalBase.py +1 -1
  100. pygeodesy/sphericalNvector.py +1 -1
  101. pygeodesy/sphericalTrigonometry.py +1 -1
  102. pygeodesy/streprs.py +6 -4
  103. pygeodesy/trf.py +1 -1
  104. pygeodesy/triaxials/__init__.py +70 -0
  105. pygeodesy/triaxials/bases.py +935 -0
  106. pygeodesy/triaxials/conformal3.py +617 -0
  107. pygeodesy/triaxials/triaxial3.py +969 -0
  108. pygeodesy/{triaxials.py → triaxials/triaxial5.py} +353 -781
  109. pygeodesy/units.py +1 -1
  110. pygeodesy/unitsBase.py +1 -1
  111. pygeodesy/ups.py +2 -3
  112. pygeodesy/utily.py +17 -14
  113. pygeodesy/utm.py +1 -1
  114. pygeodesy/utmups.py +1 -1
  115. pygeodesy/utmupsBase.py +1 -1
  116. pygeodesy/vector2d.py +1 -1
  117. pygeodesy/vector3d.py +1 -1
  118. pygeodesy/vector3dBase.py +1 -1
  119. pygeodesy/webmercator.py +1 -1
  120. pygeodesy/wgrs.py +1 -1
  121. {pygeodesy-25.11.5.dist-info → pygeodesy-25.12.12.dist-info}/METADATA +12 -12
  122. pygeodesy-25.12.12.dist-info/RECORD +125 -0
  123. pygeodesy-25.11.5.dist-info/RECORD +0 -119
  124. {pygeodesy-25.11.5.dist-info → pygeodesy-25.12.12.dist-info}/WHEEL +0 -0
  125. {pygeodesy-25.11.5.dist-info → pygeodesy-25.12.12.dist-info}/top_level.txt +0 -0
pygeodesy/karney.py CHANGED
@@ -19,19 +19,25 @@ Karney-based functionality
19
19
 
20
20
  1. The following classes and functions in C{pygeodesy}
21
21
 
22
- - L{AlbersEqualArea}, L{AlbersEqualArea2}, L{AlbersEqualArea4},
23
- L{AlbersEqualAreaCylindrical}, L{AlbersEqualAreaNorth}, L{AlbersEqualAreaSouth} --
24
- U{AlbersEqualArea<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1AlbersEqualArea.html>}
22
+ - L{AlbersEqualArea}, L{AlbersEqualArea2}, L{AlbersEqualArea4}, L{AlbersEqualAreaCylindrical},
23
+ L{AlbersEqualAreaNorth}, L{AlbersEqualAreaSouth} -- U{AlbersEqualArea
24
+ <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1AlbersEqualArea.html>}
25
25
 
26
- - L{AuxAngle}, L{AuxDST}, L{AuxDLat}, L{AuxLat} -- U{AuxAngle
27
- <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1AuxAngle.html>},
28
- U{DST<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1DST.html>},
29
- U{DAuxLatitude<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1DAuxLatitude.html>},
30
- U{AuxLatitude<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1AuxLatitude.html>} in I{GeographicLib 2.2+}
26
+ - L{Ang}, L{Deg}, L{Rad} -- U{AngleT<https://GeographicLib.SourceForge.io/C++/doc/
27
+ classGeographicLib_1_1AngleT.html>} in I{GeographicLib 2.7}.
28
+
29
+ - L{AuxAngle}, L{AuxDST}, L{AuxDLat}, L{AuxLat} -- U{AuxAngle<https://GeographicLib.SourceForge.io/C++/doc/
30
+ classGeographicLib_1_1AuxAngle.html>}, U{DST<https://GeographicLib.SourceForge.io/C++/doc/
31
+ classGeographicLib_1_1DST.html>}, U{DAuxLatitude<https://GeographicLib.SourceForge.io/C++/doc/
32
+ classGeographicLib_1_1DAuxLatitude.html>}, U{AuxLatitude<https://GeographicLib.SourceForge.io/C++/doc/
33
+ classGeographicLib_1_1AuxLatitude.html>} in I{GeographicLib 2.2+}
31
34
 
32
35
  - L{CassiniSoldner} -- U{CassiniSoldner<https://GeographicLib.SourceForge.io/C++/doc/
33
36
  classGeographicLib_1_1CassiniSoldner.html>}
34
37
 
38
+ - L{Conformal3}, L{Conformal3B}, L{Conformal3Sphere} -- U{Conformal3<https://GeographicLib.SourceForge.io/C++/doc/
39
+ classGeographicLib_1_1Triaxial_1_1Conformal3.html>} in I{GeographicLib 2.7}
40
+
35
41
  - L{EcefKarney} -- U{Geocentric<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Geocentric.html>}
36
42
 
37
43
  - L{Elliptic} -- U{EllipticFunction<https://GeographicLib.SourceForge.io/C++/doc/
@@ -40,8 +46,8 @@ Karney-based functionality
40
46
  - L{EquidistantExact}, L{EquidistantGeodSolve}, L{EquidistantKarney} -- U{AzimuthalEquidistant
41
47
  <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1AzimuthalEquidistant.html>}
42
48
 
43
- - L{Etm}, L{ExactTransverseMercator} -- U{TransverseMercatorExact
44
- <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1TransverseMercatorExact.html>}
49
+ - L{Etm}, L{ExactTransverseMercator} -- U{TransverseMercatorExact<https://GeographicLib.SourceForge.io/C++/doc/
50
+ classGeographicLib_1_1TransverseMercatorExact.html>}
45
51
 
46
52
  - L{Geodesic}, L{GeodesicLine} -- I{wrapped} U{geodesic.Geodesic<https://PyPI.org/project/geographiclib>},
47
53
  I{wrapped} U{geodesicline.GeodesicLine<https://PyPI.org/project/geographiclib>}
@@ -60,29 +66,32 @@ Karney-based functionality
60
66
  - L{GnomonicExact}, L{GnomonicGeodSolve}, L{GnomonicKarney} -- U{Gnomonic
61
67
  <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Gnomonic.html>}
62
68
 
63
- - L{Intersector} -- U{Intersect
64
- <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Intersect.html>} from I{GeographicLib 2.3+}
69
+ - L{Intersector} -- U{Intersect<https://GeographicLib.SourceForge.io/C++/doc/
70
+ classGeographicLib_1_1Intersect.html>} from I{GeographicLib 2.3+}
65
71
 
66
72
  - L{JacobiConformal} -- U{JacobiConformal
67
73
  <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1experimental_1_1JacobiConformal.html>}
68
74
 
69
- - L{KTransverseMercator} - U{TransverseMercator
70
- <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1TransverseMercator.html>}
75
+ - L{KTransverseMercator} - U{TransverseMercator<https://GeographicLib.SourceForge.io/C++/doc/
76
+ classGeographicLib_1_1TransverseMercator.html>}
71
77
 
72
78
  - L{LocalCartesian}, L{Ltp} -- U{LocalCartesian<https://GeographicLib.SourceForge.io/C++/doc/
73
79
  classGeographicLib_1_1LocalCartesian.html>}
74
80
 
75
81
  - L{Osgr} -- U{OSGB<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1OSGB.html>}
76
82
 
77
- - L{rhumb.aux_}, L{RhumbAux}, L{RhumbLineAux} -- U{Rhumb
78
- <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Rhumb.html>} and U{RhumbLine
79
- <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1RhumbLine.html>} from I{GeographicLib 2.2+}
83
+ - L{rhumb.aux_}, L{RhumbAux}, L{RhumbLineAux} -- U{Rhumb<https://GeographicLib.SourceForge.io/C++/doc/
84
+ classGeographicLib_1_1Rhumb.html>} and U{RhumbLine<https://GeographicLib.SourceForge.io/C++/doc/
85
+ classGeographicLib_1_1RhumbLine.html>} from I{GeographicLib 2.2+}
80
86
 
81
- - L{rhumb.ekx}, L{Rhumb}, L{RhumbLine} -- U{Rhumb
82
- <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Rhumb.html>},
83
- U{RhumbLine<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1RhumbLine.html>},
84
- U{TransverseMercator<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1TransverseMercator.html>}
85
- from I{GeographicLib 2.0}
87
+ - L{rhumb.ekx}, L{Rhumb}, L{RhumbLine} -- U{Rhumb<https://GeographicLib.SourceForge.io/C++/doc/
88
+ classGeographicLib_1_1Rhumb.html>}, U{RhumbLine<https://GeographicLib.SourceForge.io/C++/doc/
89
+ classGeographicLib_1_1RhumbLine.html>}, U{TransverseMercator<https://GeographicLib.SourceForge.io/C++/doc/
90
+ classGeographicLib_1_1TransverseMercator.html>} from I{GeographicLib 2.0}
91
+
92
+ - L{Triaxial3}, L{Triaxial3B} -- U{Cartesian3<https://GeographicLib.SourceForge.io/C++/doc/
93
+ classGeographicLib_1_1Triaxial_1_1Cartesian3.html>}, U{Ellipsoidal3<https://GeographicLib.SourceForge.io/C++/doc/
94
+ classGeographicLib_1_1Triaxial_1_1Ellipsoidal3.html>} in I{GeographicLib 2.7}
86
95
 
87
96
  - L{Ups} -- U{PolarStereographic<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1PolarStereographic.html>}
88
97
 
@@ -90,21 +99,22 @@ Karney-based functionality
90
99
 
91
100
  - L{UtmUps}, L{Epsg} -- U{UTMUPS<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1UTMUPS.html>}
92
101
 
93
- - L{atan1d}, L{atan2d}, L{sincos2}, L{sincos2d}, L{tand} -- U{geomath.Math
94
- <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Math.html>}
102
+ - L{atan1d}, L{atan2d}, L{sincos2}, L{sincos2d}, L{tand} -- U{geomath.Math<https://GeographicLib.SourceForge.io/C++/doc/
103
+ classGeographicLib_1_1Math.html>}
95
104
 
96
105
  are I{transcoded} from C++ classes in I{Karney}'s U{GeographicLib<https://GeographicLib.SourceForge.io/C++/doc/annotated.html>}.
97
106
 
98
107
  2. These C{pygeodesy} modules and classes
99
108
 
100
109
  - L{ellipsoidalGeodSolve}, L{ellipsoidalKarney}, L{geodesici}, L{geodsolve}, L{karney}, L{rhumb.solve}
101
- - L{EquidistantKarney}, L{FrechetKarney}, L{GeodesicSolve}, L{GeodesicLineSolve}, L{GnomonicGeodSolve},
102
- L{GnomonicKarney}, L{HeightIDWkarney}, L{Intersectool}
110
+ - L{EquidistantKarney}, L{FrechetKarney}, L{GeodesicSolve}, L{GeodesicLineSolve}, L{Geodesic3Solve},
111
+ L{GeodesicLine3Solve}, L{GnomonicGeodSolve}, L{GnomonicKarney}, L{HeightIDWkarney}, L{Intersectool}
103
112
 
104
113
  are or use I{wrappers} around I{Karney}'s Python U{geographiclib<https://PyPI.org/project/geographiclib>} or
105
- C++ utility U{GeodSolve<https://GeographicLib.SourceForge.io/C++/doc/GeodSolve.1.html>},
106
- U{IntersectTool<https://GeographicLib.SourceForge.io/C++/doc/IntersectTool.1.html>} or
107
- U{RhumbSolve<https://GeographicLib.SourceForge.io/C++/doc/RhumbSolve.1.html>}.
114
+ C++ utility U{GeodSolve<https://GeographicLib.SourceForge.io/C++/doc/GeodSolve.1.html>}, U{Geod3Solve
115
+ <https://GeographicLib.SourceForge.io/C++/doc/Geod3Solve.1.html>}, U{IntersectTool
116
+ <https://GeographicLib.SourceForge.io/C++/doc/IntersectTool.1.html>} or U{RhumbSolve
117
+ <https://GeographicLib.SourceForge.io/C++/doc/RhumbSolve.1.html>}.
108
118
 
109
119
  3. All C{pygeodesy} functions and methods to compute I{ellipsoidal} intersections, nearest points and trilaterations
110
120
 
@@ -147,14 +157,16 @@ from pygeodesy.basics import _copysign, _isin, isint, neg, unsigned0, \
147
157
  _xgeographiclib, _zip
148
158
  from pygeodesy.constants import NAN, _isfinite as _math_isfinite, \
149
159
  _0_0, _1_0, _2_0, _180_0, _N_180_0, _360_0
160
+ # from pygeodesy.deprecated.classes import Rhumb7Tuple # _MODS
150
161
  from pygeodesy.errors import GeodesicError, _ValueError, _xkwds
162
+ # from pygeodesy.geod3Solve import Geod3Solve8Tuple # _MODS
151
163
  from pygeodesy.fmath import cbrt, fremainder, norm2 # Fhorner, Fsum
152
164
  from pygeodesy.internals import _getenv, _popen2, _PYGEODESY_ENV, typename, \
153
165
  _version_info
154
- from pygeodesy.interns import NN, _a12_, _area_, _azi1_, _azi2_, _azi12_, \
155
- _composite_, _lat1_, _lat2_, _lon1_, _lon2_, \
156
- _m12_, _M12_, _M21_, _number_, _s12_, _S12_, \
157
- _SPACE_, _UNDER_, _X_, _1_, _2_, _BAR_ # PYCHOK used!
166
+ from pygeodesy.interns import NN, _a12_, _area_, _azi2_, _azi12_, _composite_, \
167
+ _lat1_, _lat2_, _lon1_, _lon2_, _m12_, _M12_, \
168
+ _M21_, _number_, _s12_, _S12_, _SPACE_, \
169
+ _UNDER_, _X_, _1_, _2_, _BAR_ # PYCHOK used!
158
170
  from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS, _FOR_DOCS
159
171
  from pygeodesy.named import ADict, _NamedBase, _NamedTuple, notImplemented, _Pass
160
172
  from pygeodesy.props import deprecated_method, property_RO, property_ROnce
@@ -165,7 +177,7 @@ from pygeodesy.utily import atan2d, sincos2d, tand, _unrollon, fabs
165
177
  # from math import fabs # from .utily
166
178
 
167
179
  __all__ = _ALL_LAZY.karney
168
- __version__ = '25.09.13'
180
+ __version__ = '25.12.12'
169
181
 
170
182
  _1_16th = _1_0 / 16
171
183
  _2_4_ = '2.4'
@@ -178,6 +190,11 @@ _perimeter_ = 'perimeter'
178
190
  class _GTuple(_NamedTuple): # in .testNamedTuples
179
191
  '''(INTERNAL) Helper.
180
192
  '''
193
+ def _dup_iteration(self, r):
194
+ if self._iteration is not None:
195
+ r._iteration = self._iteration
196
+ return r
197
+
181
198
  def toGDict(self, **updates): # NO name=NN
182
199
  '''Convert this C{*Tuple} to a L{GDict}.
183
200
 
@@ -186,9 +203,15 @@ class _GTuple(_NamedTuple): # in .testNamedTuples
186
203
  r = GDict(_zip(self._Names_, self)) # strict=True
187
204
  if updates:
188
205
  r.update(updates)
189
- if self._iteration is not None:
190
- r._iteration = self._iteration
191
- return r
206
+ return self._dup_iteration(r)
207
+
208
+ def _toTuple(self, nTuple, dflt, updates={}):
209
+ '''(INTERNAL) Convert this C{_GTuple} to an B{C{nTuple}}.
210
+ '''
211
+ _g = self.toGDict(**updates).get
212
+ t = tuple(_g(n, dflt) for n in nTuple._Names_)
213
+ t = nTuple(t, name=self.name)
214
+ return self._dup_iteration(t)
192
215
 
193
216
 
194
217
  class _Lat(Lat):
@@ -444,14 +467,25 @@ class GDict(ADict): # XXX _NamedDict
444
467
  '''
445
468
  return self._toTuple(_MODS.geodsolve.GeodSolve12Tuple, dflt)
446
469
 
470
+ def toGeod3Solve8Tuple(self, dflt=NAN): # PYCHOK 12 args
471
+ '''Convert this L{GDict} result to an 8-Tuple, compatible with I{Karney}'s
472
+ U{Geod3Solve<https://GeographicLib.SourceForge.io/C++/doc/Geod3Solve.1.html>}
473
+ result.
474
+
475
+ @kwarg dflt: Default value for missing items (C{any}).
476
+
477
+ @return: L{Geod3Solve8Tuple}C{(bet1, omg1, alp1, bet2, omg2, alp2, s12, a12)}.
478
+ '''
479
+ return self._toTuple(_MODS.geod3solve.Geod3Solve8Tuple, dflt)
480
+
447
481
  def toInverse10Tuple(self, dflt=NAN):
448
482
  '''Convert this L{GDict} result to a 10-tuple, like I{Karney}'s
449
483
  method C{geographiclib.geodesic.Geodesic._GenInverse}.
450
484
 
451
485
  @kwarg dflt: Default value for missing items (C{any}).
452
486
 
453
- @return: L{Inverse10Tuple}C{(a12, s12, salp1, calp1,
454
- salp2, calp2, m12, M12, M21, S12)}.
487
+ @return: L{Inverse10Tuple}C{(a12, s12, salp1, calp1, salp2, calp2,
488
+ m12, M12, M21, S12)}.
455
489
  '''
456
490
  return self._toTuple(Inverse10Tuple, dflt)
457
491
 
@@ -480,8 +514,8 @@ class GDict(ADict): # XXX _NamedDict
480
514
 
481
515
  @kwarg dflt: Default value for missing items (C{any}).
482
516
 
483
- @return: L{Rhumb8Tuple}C{(lat1, lon1, lat2, lon2,
484
- azi12, s12, S12, a12)}.
517
+ @return: L{Rhumb8Tuple}C{(lat1, lon1, lat2, lon2, azi12,
518
+ s12, S12, a12)}.
485
519
  '''
486
520
  return self._toTuple(Rhumb8Tuple, dflt)
487
521
 
@@ -498,9 +532,8 @@ class GDict(ADict): # XXX _NamedDict
498
532
  def _toTuple(self, nTuple, dflt):
499
533
  '''(INTERNAL) Convert this C{GDict} to an B{C{nTuple}}.
500
534
  '''
501
- _g = getattr
502
- t = tuple(_g(self, n, dflt) for n in nTuple._Names_)
503
- return nTuple(t, iteration=self._iteration)
535
+ t = tuple(self.get(n, dflt) for n in nTuple._Names_)
536
+ return nTuple(t, iteration=self._iteration, name=self.name) # PYCHOK name
504
537
 
505
538
  def _2X(self, gl, _2X=_X_): # .Intersectool, .Intersector
506
539
  '''(INTERNAL) Rename C{-2} attr to C{-X} or C{-M}.
@@ -523,19 +556,6 @@ class GDict(ADict): # XXX _NamedDict
523
556
  return self
524
557
 
525
558
 
526
- class GeodSolve12Tuple(_GTuple):
527
- '''12-Tuple C{(lat1, lon1, azi1, lat2, lon2, azi2, s12, a12, m12, M12, M21, S12)} with
528
- angles C{lat1}, C{lon1}, C{azi1}, C{lat2}, C{lon2} and C{azi2} and arc C{a12} all in
529
- C{degrees}, initial C{azi1} and final C{azi2} forward azimuths, distance C{s12} and
530
- reduced length C{m12} in C{meter}, area C{S12} in C{meter} I{squared} and geodesic
531
- scale factors C{M12} and C{M21}, both C{scalar}, see U{GeodSolve
532
- <https://GeographicLib.SourceForge.io/C++/doc/GeodSolve.1.html>}.
533
- '''
534
- # from GeodSolve --help option -f ... lat1 lon1 azi1 lat2 lon2 azi2 s12 a12 m12 M12 M21 S12
535
- _Names_ = (_lat1_, _lon1_, _azi1_, _lat2_, _lon2_, _azi2_, _s12_, _a12_, _m12_, _M12_, _M21_, _S12_)
536
- _Units_ = (_Lat, _Lon, _Azi, _Lat, _Lon, _Azi, _M, _Deg, _Pass, _Pass, _Pass, _M2)
537
-
538
-
539
559
  class Inverse10Tuple(_GTuple):
540
560
  '''10-Tuple C{(a12, s12, salp1, calp1, salp2, calp2, m12, M12, M21, S12)} with arc length
541
561
  C{a12} in C{degrees}, distance C{s12} and reduced length C{m12} in C{meter}, area
@@ -555,6 +575,56 @@ class Inverse10Tuple(_GTuple):
555
575
  **updates) # PYCHOK indent
556
576
 
557
577
 
578
+ class Rhumb8Tuple(_GTuple):
579
+ '''8-Tuple C{(lat1, lon1, lat2, lon2, azi12, s12, S12, a12)} with lat- C{lat1},
580
+ C{lat2} and longitudes C{lon1}, C{lon2} of both points, the azimuth of the
581
+ rhumb line C{azi12}, the distance C{s12}, the area C{S12} under the rhumb
582
+ line and the angular distance C{a12} between both points.
583
+ '''
584
+ _Names_ = (_lat1_, _lon1_, _lat2_, _lon2_, _azi12_, _s12_, _S12_, _a12_)
585
+ _Units_ = ( Lat, Lon, Lat, Lon, _Azi, _M, _M2, _Deg)
586
+
587
+ def toDirect9Tuple(self, dflt=NAN, **a12_azi1_azi2_m12_M12_M21):
588
+ '''Convert this L{Rhumb8Tuple} result to a 9-tuple, like I{Karney}'s
589
+ method C{geographiclib.geodesic.Geodesic._GenDirect}.
590
+
591
+ @kwarg dflt: Default value for missing items (C{any}).
592
+ @kwarg a12_azi1_azi2_m12_M12_M21: Optional keyword arguments
593
+ to specify or override L{Inverse10Tuple} items.
594
+
595
+ @return: L{Direct9Tuple}C{(a12, lat2, lon2, azi2, s12,
596
+ m12, M12, M21, S12)}
597
+ '''
598
+ d = dict(azi1=self.azi12, M12=_1_0, m12=self.s12, # PYCHOK attr
599
+ azi2=self.azi12, M21=_1_0) # PYCHOK attr
600
+ if a12_azi1_azi2_m12_M12_M21:
601
+ d.update(a12_azi1_azi2_m12_M12_M21)
602
+ return self._toTuple(Direct9Tuple, dflt, d)
603
+
604
+ def toInverse10Tuple(self, dflt=NAN, **a12_m12_M12_M21_salp1_calp1_salp2_calp2):
605
+ '''Convert this L{Rhumb8Tuple} to a 10-tuple, like I{Karney}'s
606
+ method C{geographiclib.geodesic.Geodesic._GenInverse}.
607
+
608
+ @kwarg dflt: Default value for missing items (C{any}).
609
+ @kwarg a12_m12_M12_M21_salp1_calp1_salp2_calp2: Optional keyword
610
+ arguments to specify or override L{Inverse10Tuple} items.
611
+
612
+ @return: L{Inverse10Tuple}C{(a12, s12, salp1, calp1, salp2, calp2,
613
+ m12, M12, M21, S12)}.
614
+ '''
615
+ s, c = _sincos2d(self.azi12) # PYCHOK attr
616
+ d = dict(salp1=s, calp1=c, M12=_1_0, m12=self.s12, # PYCHOK attr
617
+ salp2=s, calp2=c, M21=_1_0)
618
+ if a12_m12_M12_M21_salp1_calp1_salp2_calp2:
619
+ d.update(a12_m12_M12_M21_salp1_calp1_salp2_calp2)
620
+ return self._toTuple(Inverse10Tuple, dflt, d)
621
+
622
+ @deprecated_method
623
+ def _to7Tuple(self): # in DEPRECATED Rhumb7Tuple
624
+ '''DEPRECATED, I{do not use!}'''
625
+ return _MODS.deprecated.classes.Rhumb7Tuple(self[:-1])
626
+
627
+
558
628
  class _kWrapped(_CapsBase): # in .geodesicw
559
629
  '''(INTERNAL) Wrapper for some of I{Karney}'s U{geographiclib
560
630
  <https://PyPI.org/project/geographiclib>} classes.
@@ -608,67 +678,10 @@ class _kWrapped(_CapsBase): # in .geodesicw
608
678
  _wrapped = _kWrapped() # PYCHOK singleton, .datum, .test/base.py
609
679
 
610
680
 
611
- class Rhumb8Tuple(_GTuple):
612
- '''8-Tuple C{(lat1, lon1, lat2, lon2, azi12, s12, S12, a12)} with lat- C{lat1},
613
- C{lat2} and longitudes C{lon1}, C{lon2} of both points, the azimuth of the
614
- rhumb line C{azi12}, the distance C{s12}, the area C{S12} under the rhumb
615
- line and the angular distance C{a12} between both points.
616
- '''
617
- _Names_ = (_lat1_, _lon1_, _lat2_, _lon2_, _azi12_, _s12_, _S12_, _a12_)
618
- _Units_ = ( Lat, Lon, Lat, Lon, _Azi, _M, _M2, _Deg)
619
-
620
- def toDirect9Tuple(self, dflt=NAN, **a12_azi1_azi2_m12_M12_M21):
621
- '''Convert this L{Rhumb8Tuple} result to a 9-tuple, like I{Karney}'s
622
- method C{geographiclib.geodesic.Geodesic._GenDirect}.
623
-
624
- @kwarg dflt: Default value for missing items (C{any}).
625
- @kwarg a12_azi1_azi2_m12_M12_M21: Optional keyword arguments
626
- to specify or override L{Inverse10Tuple} items.
627
-
628
- @return: L{Direct9Tuple}C{(a12, lat2, lon2, azi2, s12,
629
- m12, M12, M21, S12)}
630
- '''
631
- d = dict(azi1=self.azi12, M12=_1_0, m12=self.s12, # PYCHOK attr
632
- azi2=self.azi12, M21=_1_0) # PYCHOK attr
633
- if a12_azi1_azi2_m12_M12_M21:
634
- d.update(a12_azi1_azi2_m12_M12_M21)
635
- return self._toTuple(Direct9Tuple, dflt, d)
636
-
637
- def toInverse10Tuple(self, dflt=NAN, **a12_m12_M12_M21_salp1_calp1_salp2_calp2):
638
- '''Convert this L{Rhumb8Tuple} to a 10-tuple, like I{Karney}'s
639
- method C{geographiclib.geodesic.Geodesic._GenInverse}.
640
-
641
- @kwarg dflt: Default value for missing items (C{any}).
642
- @kwarg a12_m12_M12_M21_salp1_calp1_salp2_calp2: Optional keyword
643
- arguments to specify or override L{Inverse10Tuple} items.
644
-
645
- @return: L{Inverse10Tuple}C{(a12, s12, salp1, calp1, salp2, calp2,
646
- m12, M12, M21, S12)}.
647
- '''
648
- s, c = sincos2d(self.azi12) # PYCHOK attr
649
- d = dict(salp1=s, calp1=c, M12=_1_0, m12=self.s12, # PYCHOK attr
650
- salp2=s, calp2=c, M21=_1_0)
651
- if a12_m12_M12_M21_salp1_calp1_salp2_calp2:
652
- d.update(a12_m12_M12_M21_salp1_calp1_salp2_calp2)
653
- return self._toTuple(Inverse10Tuple, dflt, d)
654
-
655
- def _toTuple(self, nTuple, dflt, updates={}):
656
- '''(INTERNAL) Convert this C{Rhumb8Tuple} to an B{C{nTuple}}.
657
- '''
658
- _g = self.toGDict(**updates).get
659
- t = tuple(_g(n, dflt) for n in nTuple._Names_)
660
- return nTuple(t, name=self.name)
661
-
662
- @deprecated_method
663
- def _to7Tuple(self):
664
- '''DEPRECATED, do not use!'''
665
- return _MODS.deprecated.classes.Rhumb7Tuple(self[:-1])
666
-
667
-
668
681
  class _Xables(object):
669
682
  '''(INTERNAL) Get I{Karney}'s executable paths from/and env vars.
670
683
  '''
671
- bin_ = '/opt/local/bin/' # '/opt/local/Cellar/geographiclib/2.3/bin/' # HomeBrew on macOS
684
+ bin_ = '/opt/local/bin/' # '/opt/local/Cellar/geographiclib/2.X/bin/' # HomeBrew on macOS
672
685
  ENV = NN
673
686
 
674
687
  def GeoConvert(self, *dir_):
@@ -677,6 +690,9 @@ class _Xables(object):
677
690
  def GeodSolve(self, *dir_):
678
691
  return self._path(self.GeodSolve, *dir_)
679
692
 
693
+ def Geod3Solve(self, *dir_):
694
+ return self._path(self.Geod3Solve, *dir_)
695
+
680
696
  def IntersectTool(self, *dir_):
681
697
  return self._path(self.IntersectTool, *dir_)
682
698
 
@@ -1063,7 +1079,7 @@ __all__ += _ALL_DOCS(Caps, _CapsBase)
1063
1079
 
1064
1080
  # **) MIT License
1065
1081
  #
1066
- # Copyright (C) 2016-2025 -- mrJean1 at Gmail -- All Rights Reserved.
1082
+ # Copyright (C) 2016-2026 -- mrJean1 at Gmail -- All Rights Reserved.
1067
1083
  #
1068
1084
  # Permission is hereby granted, free of charge, to any person obtaining a
1069
1085
  # copy of this software and associated documentation files (the "Software"),
pygeodesy/ktm.py CHANGED
@@ -612,7 +612,7 @@ if __name__ == _DMAIN_:
612
612
 
613
613
  # **) MIT License
614
614
  #
615
- # Copyright (C) 2022-2025 -- mrJean1 at Gmail -- All Rights Reserved.
615
+ # Copyright (C) 2022-2026 -- mrJean1 at Gmail -- All Rights Reserved.
616
616
  #
617
617
  # Permission is hereby granted, free of charge, to any person obtaining a
618
618
  # copy of this software and associated documentation files (the "Software"),
pygeodesy/latlonBase.py CHANGED
@@ -1672,7 +1672,7 @@ __all__ += _ALL_DOCS(LatLonBase)
1672
1672
 
1673
1673
  # **) MIT License
1674
1674
  #
1675
- # Copyright (C) 2016-2025 -- mrJean1 at Gmail -- All Rights Reserved.
1675
+ # Copyright (C) 2016-2026 -- mrJean1 at Gmail -- All Rights Reserved.
1676
1676
  #
1677
1677
  # Permission is hereby granted, free of charge, to any person obtaining a
1678
1678
  # copy of this software and associated documentation files (the "Software"),
pygeodesy/lazily.py CHANGED
@@ -179,6 +179,7 @@ _ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY',
179
179
  albers=_a('AlbersEqualArea', 'AlbersEqualArea2', 'AlbersEqualArea4',
180
180
  'AlbersEqualAreaCylindrical', 'AlbersEqualAreaNorth', 'AlbersEqualAreaSouth',
181
181
  'AlbersError', 'Albers7Tuple'),
182
+ angles=_a('Ang', 'Deg', 'Lambertian', 'Rad', 'isAng'),
182
183
  auxilats=_a(), # module only
183
184
  azimuthal=_a('AzimuthalError', 'Azimuthal7Tuple',
184
185
  'Equidistant', 'EquidistantExact', 'EquidistantGeodSolve', 'EquidistantKarney',
@@ -199,9 +200,9 @@ _ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY',
199
200
  'clipCS4', 'clipFHP4', 'clipGH4', 'clipLB6', 'clipSH', 'clipSH3'),
200
201
  css=_a('CassiniSoldner', 'Css', 'CSSError', 'toCss',
201
202
  'EasNorAziRk4Tuple', 'EasNorAziRkEqu6Tuple', 'LatLonAziRk4Tuple'),
202
- constants=_a('DIG', 'EPS', 'EPS0', 'EPS02', 'EPS1', 'EPS2', 'EPS4', 'EPS_2',
203
+ constants=_a('DIG', 'EPS', 'EPS0', 'EPS02', 'EPS1', 'EPS2', 'EPS4', 'EPS8', 'EPS_2',
203
204
  'INF', 'INT0', 'MANT_DIG', 'MAX', 'MAX_EXP', 'MIN', 'MIN_EXP', 'NAN', 'NEG0', 'NINF',
204
- 'PI', 'PI2', 'PI_2', 'PI3', 'PI_3', 'PI3_2', 'PI4', 'PI_4', 'PI_6',
205
+ 'OVERFLOW', 'PI', 'PI2', 'PI_2', 'PI3', 'PI_3', 'PI3_2', 'PI4', 'PI_4', 'PI_6',
205
206
  'R_FM', 'R_GM', 'R_KM', 'R_M', 'R_MA', 'R_MB', 'R_NM', 'R_QM', 'R_SM', 'R_VM',
206
207
  'float_', 'float0_', 'floats_', 'isclose', 'isfinite', 'isinf', 'isint0',
207
208
  'isnan', 'isnear0', 'isnear1', 'isnear90', 'isneg', 'isneg0', 'isninf', 'isnon0',
@@ -252,7 +253,7 @@ _ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY',
252
253
  formy=_a('Radical2Tuple',
253
254
  'angle2chord', 'antipode', 'antipode_', 'bearing', 'bearing_',
254
255
  'chord2angle', 'compassAngle', 'cosineLaw', 'cosineLaw_',
255
- 'equirectangular', 'equirectangular4', 'euclidean', 'euclidean_',
256
+ 'elliperim', 'equirectangular', 'equirectangular4', 'euclidean', 'euclidean_',
256
257
  'excessAbc_', 'excessCagnoli_', 'excessGirard_', 'excessLHuilier_',
257
258
  'excessKarney', 'excessKarney_', 'excessQuad', 'excessQuad_',
258
259
  'flatLocal', 'flatLocal_', 'flatPolar', 'flatPolar_',
@@ -275,6 +276,7 @@ _ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY',
275
276
  geodesicx=_a('gx', 'gxarea', 'gxbases', 'gxline', # modules
276
277
  'GeodesicAreaExact', 'GeodesicExact', 'GeodesicLineExact', 'PolygonArea'),
277
278
  geodsolve=_a('GeodesicSolve', 'GeodesicLineSolve', 'GeodSolve12Tuple'),
279
+ geod3solve=_a('Geodesic3Solve', 'GeodesicLine3Solve', 'Geod3Solve8Tuple', 'Geodesic3Error'),
278
280
  geohash=_a('Geohash', 'Geohashed', 'GeohashError', 'Neighbors8Dict', 'Resolutions2Tuple', 'Sizes3Tuple'),
279
281
  geoids=_a('GeoidError', 'GeoidEGM96', 'GeoidG2012B', 'GeoidKarney', 'GeoidPGM', 'egmGeoidHeights',
280
282
  'PGMError', 'GeoidHeight5Tuple'),
@@ -291,7 +293,7 @@ _ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY',
291
293
  interns=_interns.__all__,
292
294
  iters=_a('LatLon2PsxyIter', 'PointsIter', 'points2',
293
295
  'isNumpy2', 'isPoints2', 'isTuple2', 'iterNumpy2', 'iterNumpy2over'),
294
- karney=_a('Area3Tuple', 'Caps', 'Direct9Tuple', 'GDict', 'Inverse10Tuple', 'Rhumb8Tuple'),
296
+ karney=_a('Area3Tuple', 'Caps', 'Direct9Tuple', 'GDict', 'Inverse10Tuple'),
295
297
  ktm=_a('KTMError', 'KTransverseMercator'),
296
298
  latlonBase=_a('latlon2n_xyz', 'philam2n_xyz'),
297
299
  lazily=_a('LazyAttributeError', 'LazyImportError', 'isLazy'),
@@ -336,7 +338,7 @@ _ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY',
336
338
  rhumb=_a(), # module only
337
339
  rhumb_aux_=_a('RhumbAux', 'RhumbLineAux'),
338
340
  rhumb_ekx=_a('Rhumb', 'RhumbLine'),
339
- rhumb_solve=_a('RhumbSolve', 'RhumbLineSolve', 'RhumbSolve7Tuple'),
341
+ rhumb_solve=_a('RhumbSolve', 'RhumbLineSolve', 'RhumbSolve7Tuple', 'Rhumb8Tuple'), # in .karney
340
342
  sphericalBase=_a(), # module only
341
343
  sphericalNvector=_a(), # module only
342
344
  sphericalTrigonometry=_a(), # module only
@@ -346,9 +348,15 @@ _ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY',
346
348
  'lrstrip', 'pairs', 'reprs', 'strs', 'unstr'),
347
349
  trf=_a('RefFrame', 'RefFrames', 'TransformXform', 'TRFXform', 'TRFXform7Tuple',
348
350
  'date2epoch', 'epoch2date', 'trfTransform0', 'trfTransforms', 'trfXform'),
349
- triaxials=_a('BetaOmega2Tuple', 'BetaOmega3Tuple',
350
- 'ConformalSphere', 'ConformalTriaxial', 'Conformal2Tuple',
351
- 'Triaxial', 'Triaxial_', 'TriaxialError', 'Triaxials', 'hartzell4'), # 'height4'
351
+ triaxials=_a(), # module only
352
+ triaxials_bases=_a('LLK', 'TriaxialError'),
353
+ triaxials_conformal3=_a('BetOmgGam5Tuple',
354
+ 'Conformal3', 'Conformal3B', 'Conformal3Sphere', 'Conformal5Tuple'),
355
+ triaxials_triaxial3=_a('BetOmgAlp5Tuple', 'Cartesian5Tuple', 'PhiLamZet5Tuple',
356
+ 'Triaxial3', 'Triaxial3B', 'Triaxial3s'),
357
+ triaxials_triaxial5=_a('BetaOmega2Tuple', 'BetaOmega3Tuple',
358
+ 'Conformal', 'ConformalSphere', 'Conformal2Tuple',
359
+ 'Triaxial', 'Triaxial_', 'Triaxials', 'hartzell4', 'height4'),
352
360
  units=_a('Azimuth', 'Band', 'Bearing', 'Bearing_', 'Bool',
353
361
  'Degrees', 'Degrees_', 'Degrees2', 'Distance', 'Distance_', 'Easting', 'Epoch',
354
362
  'Feet', 'FIx', 'Float_', 'Height', 'Height_', 'HeightX', 'Int_',
@@ -512,7 +520,7 @@ class _ALL_MODS(_internals._MODS_Base):
512
520
  _internals._MODS = _ALL_MODS = _ALL_MODS() # PYCHOK singleton
513
521
 
514
522
  __all__ = _ALL_LAZY.lazily
515
- __version__ = '25.10.30'
523
+ __version__ = '25.12.12'
516
524
 
517
525
 
518
526
  def _ALL_OTHER(*objs):
@@ -617,7 +625,7 @@ def _getmodinto(mod_DNAME, *Intos):
617
625
  assert isinstance(i, Intos)
618
626
  m = _MODS.getmodule(mod)
619
627
  setattr(d, _mod, m) # overwrite C{d._mod}
620
- if isLazy > 1:
628
+ if isLazy and isLazy > 1:
621
629
  t = _SPACE_(_HASH_, _imported_, m.__name__) # typename(m)
622
630
  _hash_imported(t, _MODS.into.__name__)
623
631
  assert getattr(d, _mod, None) is m
@@ -637,7 +645,7 @@ def _getmodule(name, *parent):
637
645
  def _hash_imported(t, by_into, up=3):
638
646
  '''(INTERNAL) Helper for C{_lazy_import2} and C{_ALL_MODS.into}.
639
647
  '''
640
- if isLazy > 2:
648
+ if isLazy and isLazy > 2:
641
649
  try: # see C{internals._caller3}
642
650
  _, f, s = _caller3(up)
643
651
  t = _SPACE_(t, by_into, f, _line_, s)
@@ -907,6 +915,9 @@ if __name__ == _DMAIN_:
907
915
 
908
916
  _main()
909
917
 
918
+ # % python3.14 -W ignore -m pygeodesy.lazily
919
+ # 0.061219 import vs 0.047896 _ALL_MODS: 1.28X, pygeodesy 25.12.6 Python 3.14.0 64bit arm64 macOS 26.1
920
+
910
921
  # % python3.13 -W ignore -m pygeodesy.lazily
911
922
  # 0.054235 import vs 0.052469 _ALL_MODS: 1.03X, pygeodesy 25.4.24 Python 3.13.3 64bit arm64 macOS 15.4
912
923
 
@@ -933,7 +944,7 @@ if __name__ == _DMAIN_:
933
944
 
934
945
  # **) MIT License
935
946
  #
936
- # Copyright (C) 2018-2025 -- mrJean1 at Gmail -- All Rights Reserved.
947
+ # Copyright (C) 2018-2026 -- mrJean1 at Gmail -- All Rights Reserved.
937
948
  #
938
949
  # Permission is hereby granted, free of charge, to any person obtaining a
939
950
  # copy of this software and associated documentation files (the "Software"),
pygeodesy/lcc.py CHANGED
@@ -661,7 +661,7 @@ if __name__ == _DMAIN_:
661
661
 
662
662
  # **) MIT License
663
663
  #
664
- # Copyright (C) 2016-2025 -- mrJean1 at Gmail -- All Rights Reserved.
664
+ # Copyright (C) 2016-2026 -- mrJean1 at Gmail -- All Rights Reserved.
665
665
  #
666
666
  # Permission is hereby granted, free of charge, to any person obtaining a
667
667
  # copy of this software and associated documentation files (the "Software"),
pygeodesy/ltp.py CHANGED
@@ -1128,7 +1128,7 @@ def _xLtp(ltp, *dflt):
1128
1128
 
1129
1129
  # **) MIT License
1130
1130
  #
1131
- # Copyright (C) 2016-2025 -- mrJean1 at Gmail -- All Rights Reserved.
1131
+ # Copyright (C) 2016-2026 -- mrJean1 at Gmail -- All Rights Reserved.
1132
1132
  #
1133
1133
  # Permission is hereby granted, free of charge, to any person obtaining a
1134
1134
  # copy of this software and associated documentation files (the "Software"),
pygeodesy/ltpTuples.py CHANGED
@@ -1626,7 +1626,7 @@ __all__ += _ALL_DOCS(_AbcBase)
1626
1626
 
1627
1627
  # **) MIT License
1628
1628
  #
1629
- # Copyright (C) 2016-2025 -- mrJean1 at Gmail -- All Rights Reserved.
1629
+ # Copyright (C) 2016-2026 -- mrJean1 at Gmail -- All Rights Reserved.
1630
1630
  #
1631
1631
  # Permission is hereby granted, free of charge, to any person obtaining a
1632
1632
  # copy of this software and associated documentation files (the "Software"),
pygeodesy/mgrs.py CHANGED
@@ -32,7 +32,7 @@ the UPS encoding I{at} the south and north pole.
32
32
  Set env variable C{PYGEODESY_GEOCONVERT} to the (fully qualified) path of the
33
33
  C{GeoConvert} executable to run this module as I{python[3] -m pygeodesy.mgrs}
34
34
  and compare the MGRS results with those from I{Karney}'s utility U{GeoConvert
35
- <https://GeographicLib.sourceforge.io/C++/doc/GeoConvert.1.html>}.
35
+ <https://GeographicLib.SourceForge.io/C++/doc/GeoConvert.1.html>}.
36
36
  '''
37
37
 
38
38
  from pygeodesy.basics import halfs2, _isin, _splituple, _xinstanceof
@@ -660,7 +660,7 @@ if __name__ == _DMAIN_:
660
660
 
661
661
  # from math import fabs # from .ellipsoidalVincenty
662
662
 
663
- # <https://GeographicLib.sourceforge.io/C++/doc/GeoConvert.1.html>
663
+ # <https://GeographicLib.SourceForge.io/C++/doc/GeoConvert.1.html>
664
664
  G = _Xables.GeoConvert(_Xables.bin_)
665
665
  if _Xables.X_OK(G):
666
666
  from pygeodesy.internals import _popen2
@@ -732,7 +732,7 @@ if __name__ == _DMAIN_:
732
732
 
733
733
  # **) MIT License
734
734
  #
735
- # Copyright (C) 2016-2025 -- mrJean1 at Gmail -- All Rights Reserved.
735
+ # Copyright (C) 2016-2026 -- mrJean1 at Gmail -- All Rights Reserved.
736
736
  #
737
737
  # Permission is hereby granted, free of charge, to any person obtaining a
738
738
  # copy of this software and associated documentation files (the "Software"),
pygeodesy/named.py CHANGED
@@ -17,9 +17,9 @@ 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
19
  from pygeodesy.errors import _AssertionError, _AttributeError, _ImmutableError, \
20
- _incompatible, _IndexError, _KeyError, LenError, \
21
- _NameError, _NotImplementedError, _TypeError, \
22
- _TypesError, _UnexpectedError, UnitError, _ValueError, \
20
+ _incompatible, _KeyError, LenError, _NameError, \
21
+ _NotImplementedError, _TypeError, _TypesError, \
22
+ _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
@@ -35,7 +35,7 @@ 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.09.04'
38
+ __version__ = '25.11.29'
39
39
 
40
40
  _COMMANL_ = _COMMA_ + _NL_
41
41
  _COMMASPACEDOT_ = _COMMASPACE_ + _DOT_
@@ -987,10 +987,14 @@ class _NamedTuple(tuple, _Named):
987
987
  '''
988
988
  try:
989
989
  return tuple.__getitem__(self, self._Names_.index(name))
990
- except IndexError as x:
991
- raise _IndexError(self._DOT_(name), cause=x)
990
+ except IndexError: # as x:
991
+ pass # raise _IndexError(self._DOT_(name), cause=x)
992
992
  except ValueError: # e.g. _iteration
993
- return tuple.__getattr__(self, name) # __getattribute__
993
+ try: # tuple has no __getattr__?
994
+ return tuple.__getattr__(self, name) # __getattribute__?
995
+ except AttributeError:
996
+ pass
997
+ raise _AttributeError(self._DOT_(name), txt=repr(self))
994
998
 
995
999
  # def __getitem__(self, index): # index, slice, etc.
996
1000
  # '''Get the item(s) at an B{C{index}} or slice.
@@ -1103,7 +1107,8 @@ class _NamedTuple(tuple, _Named):
1103
1107
 
1104
1108
  @return: Tuple items (C{str}).
1105
1109
  '''
1106
- return Fmt.PAREN(sep.join(reprs(self, prec=prec, fmt=fmt)))
1110
+ t = reprs(self, prec=prec, fmt=fmt)
1111
+ return Fmt.PAREN(sep.join(t)) if sep else t
1107
1112
 
1108
1113
  def toUnits(self, Error=UnitError, **name): # overloaded in .frechet, .hausdorff
1109
1114
  '''Return a copy of this C{Named-Tuple} with each item value wrapped
@@ -1468,7 +1473,7 @@ __all__ += _ALL_DOCS(_Named,
1468
1473
 
1469
1474
  # **) MIT License
1470
1475
  #
1471
- # Copyright (C) 2016-2025 -- mrJean1 at Gmail -- All Rights Reserved.
1476
+ # Copyright (C) 2016-2026 -- mrJean1 at Gmail -- All Rights Reserved.
1472
1477
  #
1473
1478
  # Permission is hereby granted, free of charge, to any person obtaining a
1474
1479
  # copy of this software and associated documentation files (the "Software"),