pygeodesy 24.10.24__py2.py3-none-any.whl → 24.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 (118) hide show
  1. {PyGeodesy-24.10.24.dist-info → PyGeodesy-24.12.12.dist-info}/METADATA +6 -6
  2. PyGeodesy-24.12.12.dist-info/RECORD +118 -0
  3. {PyGeodesy-24.10.24.dist-info → PyGeodesy-24.12.12.dist-info}/WHEEL +1 -1
  4. pygeodesy/__init__.py +5 -5
  5. pygeodesy/__main__.py +1 -1
  6. pygeodesy/albers.py +5 -5
  7. pygeodesy/auxilats/_CX_4.py +1 -1
  8. pygeodesy/auxilats/_CX_6.py +1 -1
  9. pygeodesy/auxilats/_CX_8.py +1 -1
  10. pygeodesy/auxilats/_CX_Rs.py +1 -1
  11. pygeodesy/auxilats/__init__.py +1 -1
  12. pygeodesy/auxilats/__main__.py +1 -1
  13. pygeodesy/auxilats/auxAngle.py +5 -5
  14. pygeodesy/auxilats/auxDLat.py +6 -6
  15. pygeodesy/auxilats/auxDST.py +2 -2
  16. pygeodesy/auxilats/auxLat.py +5 -5
  17. pygeodesy/auxilats/auxily.py +2 -2
  18. pygeodesy/azimuthal.py +55 -65
  19. pygeodesy/basics.py +35 -34
  20. pygeodesy/booleans.py +37 -37
  21. pygeodesy/cartesianBase.py +26 -65
  22. pygeodesy/clipy.py +1 -1
  23. pygeodesy/constants.py +7 -7
  24. pygeodesy/css.py +8 -9
  25. pygeodesy/datums.py +1 -1
  26. pygeodesy/deprecated/__init__.py +2 -2
  27. pygeodesy/deprecated/bases.py +1 -1
  28. pygeodesy/deprecated/classes.py +10 -10
  29. pygeodesy/deprecated/consterns.py +1 -1
  30. pygeodesy/deprecated/datum.py +1 -1
  31. pygeodesy/deprecated/functions.py +23 -13
  32. pygeodesy/deprecated/nvector.py +1 -1
  33. pygeodesy/deprecated/rhumbBase.py +1 -1
  34. pygeodesy/deprecated/rhumbaux.py +1 -1
  35. pygeodesy/deprecated/rhumbsolve.py +1 -1
  36. pygeodesy/deprecated/rhumbx.py +1 -1
  37. pygeodesy/dms.py +1 -1
  38. pygeodesy/ecef.py +63 -69
  39. pygeodesy/elevations.py +1 -1
  40. pygeodesy/ellipsoidalBase.py +106 -121
  41. pygeodesy/ellipsoidalBaseDI.py +115 -119
  42. pygeodesy/ellipsoidalExact.py +36 -38
  43. pygeodesy/ellipsoidalGeodSolve.py +1 -1
  44. pygeodesy/ellipsoidalKarney.py +1 -1
  45. pygeodesy/ellipsoidalNvector.py +1 -1
  46. pygeodesy/ellipsoidalVincenty.py +6 -5
  47. pygeodesy/ellipsoids.py +7 -8
  48. pygeodesy/elliptic.py +6 -6
  49. pygeodesy/epsg.py +1 -1
  50. pygeodesy/errors.py +25 -25
  51. pygeodesy/etm.py +84 -76
  52. pygeodesy/fmath.py +54 -51
  53. pygeodesy/formy.py +74 -106
  54. pygeodesy/frechet.py +1 -1
  55. pygeodesy/fstats.py +1 -1
  56. pygeodesy/fsums.py +82 -72
  57. pygeodesy/gars.py +1 -1
  58. pygeodesy/geodesici.py +4 -4
  59. pygeodesy/geodesicw.py +16 -15
  60. pygeodesy/geodesicx/_C4_24.py +2 -2
  61. pygeodesy/geodesicx/_C4_27.py +2 -2
  62. pygeodesy/geodesicx/_C4_30.py +2 -2
  63. pygeodesy/geodesicx/__init__.py +3 -3
  64. pygeodesy/geodesicx/__main__.py +1 -1
  65. pygeodesy/geodesicx/gx.py +6 -5
  66. pygeodesy/geodesicx/gxarea.py +2 -2
  67. pygeodesy/geodesicx/gxbases.py +2 -2
  68. pygeodesy/geodesicx/gxline.py +16 -12
  69. pygeodesy/geodsolve.py +8 -17
  70. pygeodesy/geohash.py +1 -1
  71. pygeodesy/geoids.py +6 -6
  72. pygeodesy/hausdorff.py +1 -1
  73. pygeodesy/heights.py +3 -3
  74. pygeodesy/internals.py +64 -80
  75. pygeodesy/interns.py +2 -3
  76. pygeodesy/iters.py +1 -1
  77. pygeodesy/karney.py +4 -4
  78. pygeodesy/ktm.py +20 -21
  79. pygeodesy/latlonBase.py +296 -346
  80. pygeodesy/lazily.py +15 -15
  81. pygeodesy/lcc.py +5 -5
  82. pygeodesy/ltp.py +55 -59
  83. pygeodesy/ltpTuples.py +208 -192
  84. pygeodesy/mgrs.py +9 -10
  85. pygeodesy/named.py +153 -3
  86. pygeodesy/namedTuples.py +58 -7
  87. pygeodesy/nvectorBase.py +122 -105
  88. pygeodesy/osgr.py +10 -13
  89. pygeodesy/points.py +1 -1
  90. pygeodesy/props.py +3 -3
  91. pygeodesy/resections.py +26 -26
  92. pygeodesy/rhumb/__init__.py +2 -2
  93. pygeodesy/rhumb/aux_.py +2 -2
  94. pygeodesy/rhumb/bases.py +2 -2
  95. pygeodesy/rhumb/ekx.py +4 -4
  96. pygeodesy/rhumb/solve.py +4 -4
  97. pygeodesy/simplify.py +291 -403
  98. pygeodesy/solveBase.py +1 -1
  99. pygeodesy/sphericalBase.py +1 -1
  100. pygeodesy/sphericalNvector.py +84 -127
  101. pygeodesy/sphericalTrigonometry.py +66 -71
  102. pygeodesy/streprs.py +10 -5
  103. pygeodesy/trf.py +1 -1
  104. pygeodesy/triaxials.py +23 -16
  105. pygeodesy/units.py +17 -17
  106. pygeodesy/unitsBase.py +1 -1
  107. pygeodesy/ups.py +4 -4
  108. pygeodesy/utily.py +202 -145
  109. pygeodesy/utm.py +10 -10
  110. pygeodesy/utmups.py +1 -1
  111. pygeodesy/utmupsBase.py +1 -1
  112. pygeodesy/vector2d.py +17 -17
  113. pygeodesy/vector3d.py +32 -23
  114. pygeodesy/vector3dBase.py +22 -19
  115. pygeodesy/webmercator.py +5 -5
  116. pygeodesy/wgrs.py +5 -5
  117. PyGeodesy-24.10.24.dist-info/RECORD +0 -118
  118. {PyGeodesy-24.10.24.dist-info → PyGeodesy-24.12.12.dist-info}/top_level.txt +0 -0
pygeodesy/nvectorBase.py CHANGED
@@ -11,14 +11,14 @@ and published under the same MIT Licence**, see U{Vector-based geodesy
11
11
  '''
12
12
 
13
13
  # from pygeodesy.basics import map1 # from .namedTuples
14
- from pygeodesy.constants import EPS, EPS1, EPS_2, R_M, _2_0, _N_2_0
14
+ from pygeodesy.constants import EPS, EPS0, EPS1, EPS_2, R_M, \
15
+ _0_0, _1_0, _2_0, _N_2_0
15
16
  # from pygeodesy.datums import _spherical_datum # from .formy
16
17
  from pygeodesy.errors import IntersectionError, _ValueError, VectorError, \
17
18
  _xattrs, _xkwds, _xkwds_pop2
18
- from pygeodesy.fmath import fdot, fidw, hypot_ # PYCHOK fdot shared
19
+ from pygeodesy.fmath import fdot, fidw, hypot # PYCHOK fdot shared
19
20
  from pygeodesy.fsums import Fsum, fsumf_
20
- from pygeodesy.formy import _isequalTo, n_xyz2latlon, n_xyz2philam, \
21
- _spherical_datum
21
+ from pygeodesy.formy import _isequalTo, _spherical_datum
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_, \
@@ -26,23 +26,23 @@ from pygeodesy.interns import NN, _1_, _2_, _3_, _bearing_, _coincident_, \
26
26
  from pygeodesy.latlonBase import LatLonBase, _ALL_DOCS, _ALL_LAZY, _MODS
27
27
  # from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS # from .latlonBase
28
28
  from pygeodesy.named import _xother3, _under
29
- from pygeodesy.namedTuples import Trilaterate5Tuple, Vector3Tuple, \
30
- Vector4Tuple, map1
29
+ from pygeodesy.namedTuples import LatLon2Tuple, PhiLam2Tuple, Trilaterate5Tuple, \
30
+ Vector3Tuple, Vector4Tuple, map1
31
31
  from pygeodesy.props import deprecated_method, Property_RO, property_doc_, \
32
32
  property_RO, property_ROnce, _update_all
33
33
  from pygeodesy.streprs import Fmt, hstr, unstr
34
34
  from pygeodesy.units import Bearing, Height, Radius_, Scalar
35
- from pygeodesy.utily import sincos2d, _unrollon, _unrollon3
35
+ from pygeodesy.utily import atan2, sincos2d, _unrollon, _unrollon3
36
36
  from pygeodesy.vector3d import Vector3d, _xyzhdlln4
37
37
 
38
- from math import fabs, sqrt
38
+ from math import degrees, fabs, sqrt
39
39
 
40
40
  __all__ = _ALL_LAZY.nvectorBase
41
- __version__ = '24.10.12'
41
+ __version__ = '24.11.24'
42
42
 
43
43
 
44
44
  class NvectorBase(Vector3d): # XXX kept private
45
- '''Base class for ellipsoidal and spherical C{Nvector}s.
45
+ '''(INTERNAL) Base class for ellipsoidal and spherical C{Nvector}s.
46
46
  '''
47
47
  _datum = None # L{Datum}, overriden
48
48
  _h = Height(h=0) # height (C{meter})
@@ -51,8 +51,8 @@ class NvectorBase(Vector3d): # XXX kept private
51
51
  def __init__(self, x_xyz, y=None, z=None, h=0, datum=None, **ll_name):
52
52
  '''New n-vector normal to the earth's surface.
53
53
 
54
- @arg x_xyz: X component of vector (C{scalar}) or (3-D) vector
55
- (C{Nvector}, L{Vector3d}, L{Vector3Tuple} or L{Vector4Tuple}).
54
+ @arg x_xyz: X component of vector (C{scalar}) or (3-D) vector (C{Nvector},
55
+ L{Vector3d}, L{Vector3Tuple} or L{Vector4Tuple}).
56
56
  @kwarg y: Y component of vector (C{scalar}), required if B{C{x_xyz}} is
57
57
  C{scalar} and same units as B{C{x_xyz}}, ignored otherwise.
58
58
  @kwarg z: Z component of vector (C{scalar}), like B{C{y}}.
@@ -159,7 +159,7 @@ class NvectorBase(Vector3d): # XXX kept private
159
159
  def latlon(self):
160
160
  '''Get the (geodetic) lat-, longitude in C{degrees} (L{LatLon2Tuple}C{(lat, lon)}).
161
161
  '''
162
- return n_xyz2latlon(self.x, self.y, self.z, name=self.name)
162
+ return n_xyz2latlon(self, name=self.name)
163
163
 
164
164
  @Property_RO
165
165
  def latlonheight(self):
@@ -189,7 +189,7 @@ class NvectorBase(Vector3d): # XXX kept private
189
189
  def philam(self):
190
190
  '''Get the (geodetic) lat-, longitude in C{radians} (L{PhiLam2Tuple}C{(phi, lam)}).
191
191
  '''
192
- return n_xyz2philam(self.x, self.y, self.z, name=self.name)
192
+ return n_xyz2philam(self, name=self.name)
193
193
 
194
194
  @Property_RO
195
195
  def philamheight(self):
@@ -211,89 +211,71 @@ class NvectorBase(Vector3d): # XXX kept private
211
211
 
212
212
  @deprecated_method
213
213
  def to2ab(self): # PYCHOK no cover
214
- '''DEPRECATED, use property L{philam}.
215
-
216
- @return: A L{PhiLam2Tuple}C{(phi, lam)}.
217
- '''
214
+ '''DEPRECATED, use property L{philam}.'''
218
215
  return self.philam
219
216
 
220
217
  @deprecated_method
221
218
  def to3abh(self, height=None): # PYCHOK no cover
222
- '''DEPRECATED, use property L{philamheight} or C{philam.to3Tuple(B{height})}.
223
-
224
- @kwarg height: Optional height, overriding this
225
- n-vector's height (C{meter}).
226
-
227
- @return: A L{PhiLam3Tuple}C{(phi, lam, height)}.
228
-
229
- @raise ValueError: Invalid B{C{height}}.
230
- '''
219
+ '''DEPRECATED, use property L{philamheight} or C{philam.to3Tuple(B{height})}.'''
231
220
  return self.philamheight if height in (None, self.h) else \
232
221
  self.philam.to3Tuple(height)
233
222
 
234
- def toCartesian(self, h=None, Cartesian=None, datum=None, **Cartesian_kwds):
223
+ def toCartesian(self, h=None, Cartesian=None, datum=None, **name_Cartesian_kwds): # PYCHOK signature
235
224
  '''Convert this n-vector to C{Nvector}-based cartesian (ECEF) coordinates.
236
225
 
237
226
  @kwarg h: Optional height, overriding this n-vector's height (C{meter}).
238
- @kwarg Cartesian: Optional class to return the (ECEF) coordinates
239
- (C{Cartesian}).
227
+ @kwarg Cartesian: Optional class to return the (ECEF) coordinates (C{Cartesian}).
240
228
  @kwarg datum: Optional datum (C{Datum}), overriding this datum.
241
- @kwarg Cartesian_kwds: Optional, additional B{C{Cartesian}} keyword
242
- arguments, ignored if C{B{Cartesian} is None}.
229
+ @kwarg name_Cartesian_kwds: Optional C{B{name}=NN} (C{str}) and optionally, additional
230
+ B{C{Cartesian}} keyword arguments, ignored if C{B{Cartesian} is None}.
243
231
 
244
- @return: The cartesian (ECEF) coordinates (B{C{Cartesian}}) or
245
- if C{B{Cartesian} is None}, an L{Ecef9Tuple}C{(x, y, z,
246
- lat, lon, height, C, M, datum)} with C{C} and C{M} if
247
- available.
232
+ @return: The (ECEF) coordinates (B{C{Cartesian}}) or if C{B{Cartesian} is None}, an
233
+ L{Ecef9Tuple}C{(x, y, z, lat, lon, height, C, M, datum)} with C{C} and C{M}
234
+ if available.
248
235
 
249
- @raise TypeError: Invalid B{C{Cartesian}} or B{C{Cartesian_kwds}}
250
- argument.
236
+ @raise TypeError: Invalid B{C{Cartesian}} or B{C{name_Cartesian_kwds}} argument.
251
237
 
252
238
  @raise ValueError: Invalid B{C{h}}.
253
239
  '''
254
- D = _spherical_datum(datum or self.datum, name=self.name)
255
- E = D.ellipsoid
256
- h = self.h if h is None else Height(h)
257
-
258
- x, y, z = self.x, self.y, self.z
259
- # Kenneth Gade eqn 22
260
- n = E.b / hypot_(x * E.a_b, y * E.a_b, z)
261
- r = h + n * E.a2_b2
262
-
263
- x *= r
264
- y *= r
265
- z *= h + n
240
+ if h is None:
241
+ h = self.h
242
+ elif not isinstance(h, Height):
243
+ h = Height(h=h, Error=VectorError)
244
+ _, r, v = self._toEcefDrv3(Cartesian, None, datum, h, **name_Cartesian_kwds)
245
+ if r is None:
246
+ r = v.toCartesian(Cartesian, **self._name1__(name_Cartesian_kwds)) # h=0
247
+ return r
266
248
 
267
- if Cartesian is None:
268
- r = self.Ecef(D).reverse(x, y, z, M=True)
249
+ def _toEcefDrv3(self, CC, LL, datum, h, name=NN, **unused):
250
+ '''(INTERNAL) Helper for methods C{toCartesian} and C{toLatLon}.
251
+ '''
252
+ D = self.datum if datum in (None, self.datum) else \
253
+ _spherical_datum(datum, name=self.name)
254
+ if LL is None:
255
+ v = Vector3d(self, name=name or self.name) # .toVector3d(norm=False)
256
+ E = D.ellipsoid
257
+ r = E.a_b # Kenneth Gade eqn 22
258
+ n = v.times_(r, r, _1_0).length
259
+ n = (E.b / n) if n > EPS0 else _0_0
260
+ r = E.a2_b2 * n + h # fma
261
+ v = v.times_(r, r, n + h)
262
+ r = self.Ecef(D).reverse(v, M=True) if CC is None else None
269
263
  else:
270
- kwds = _xkwds(Cartesian_kwds, datum=D) # h=0
271
- r = Cartesian(x, y, z, **kwds)
272
- return self._xnamed(r)
264
+ r = v = None
265
+ return D, r, v
273
266
 
274
267
  @deprecated_method
275
268
  def to2ll(self): # PYCHOK no cover
276
- '''DEPRECATED, use property L{latlon}.
277
-
278
- @return: A L{LatLon2Tuple}C{(lat, lon)}.
279
- '''
269
+ '''DEPRECATED, use property L{latlon}.'''
280
270
  return self.latlon
281
271
 
282
272
  @deprecated_method
283
273
  def to3llh(self, height=None): # PYCHOK no cover
284
- '''DEPRECATED, use property C{latlonheight} or C{latlon.to3Tuple(B{height})}.
285
-
286
- @kwarg height: Optional height, overriding this
287
- n-vector's height (C{meter}).
288
-
289
- @return: A L{LatLon3Tuple}C{(lat, lon, height)}.
290
-
291
- @raise ValueError: Invalid B{C{height}}.
292
- '''
274
+ '''DEPRECATED, use property C{latlonheight} or C{latlon.to3Tuple(B{height})}.'''
293
275
  return self.latlonheight if height in (None, self.h) else \
294
276
  self.latlon.to3Tuple(height)
295
277
 
296
- def toLatLon(self, height=None, LatLon=None, datum=None, **LatLon_kwds):
278
+ def toLatLon(self, height=None, LatLon=None, datum=None, **name_LatLon_kwds):
297
279
  '''Convert this n-vector to an C{Nvector}-based geodetic point.
298
280
 
299
281
  @kwarg height: Optional height, overriding this n-vector's
@@ -301,26 +283,26 @@ class NvectorBase(Vector3d): # XXX kept private
301
283
  @kwarg LatLon: Optional class to return the geodetic point
302
284
  (C{LatLon}) or C{None}.
303
285
  @kwarg datum: Optional, spherical datum (C{Datum}).
304
- @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword
305
- arguments, ignored if C{B{LatLon} is None}.
286
+ @kwarg name_LatLon_kwds: Optional C{B{name}=NN} (C{str}) and optionally,
287
+ additional B{C{LatLon}} keyword arguments, ignored if
288
+ C{B{LatLon} is None}.
306
289
 
307
290
  @return: The geodetic point (C{LatLon}) or if C{B{LatLon} is None},
308
- an L{Ecef9Tuple}C{(x, y, z, lat, lon, height, C, M,
309
- datum)} with C{C} and C{M} if available.
291
+ an L{Ecef9Tuple}C{(x, y, z, lat, lon, height, C, M, datum)}
292
+ with C{C} and C{M} if available.
310
293
 
311
- @raise TypeError: Invalid B{C{LatLon}} or B{C{LatLon_kwds}}
312
- argument.
294
+ @raise TypeError: Invalid B{C{LatLon}} or B{C{name_LatLon_kwds}} argument.
313
295
 
314
296
  @raise ValueError: Invalid B{C{height}}.
315
297
  '''
316
- d = _spherical_datum(datum or self.datum, name=self.name)
317
- h = self.h if height is None else Height(height)
318
- # use self.Cartesian(Cartesian=None) for better accuracy of the height
319
- # than self.Ecef(d).forward(self.lat, self.lon, height=h, M=True)
320
- if LatLon is None:
321
- r = self.toCartesian(h=h, Cartesian=None, datum=d)
322
- else:
323
- kwds = _xkwds(LatLon_kwds, height=h, datum=d)
298
+ h = self.h if height is None else (
299
+ height if isinstance(height, Height) else
300
+ Height(height, Error=VectorError))
301
+ # use the .toCartesian() logic for better height accuracy instead of
302
+ # r = self.Ecef(D).forward(self.lat, self.lon, height=h, M=True)
303
+ D, r, _ = self._toEcefDrv3(None, LatLon, datum, h, **name_LatLon_kwds)
304
+ if r is None:
305
+ kwds = _xkwds(name_LatLon_kwds, height=h, datum=D)
324
306
  r = LatLon(self.lat, self.lon, **self._name1__(kwds))
325
307
  return r
326
308
 
@@ -349,7 +331,7 @@ class NvectorBase(Vector3d): # XXX kept private
349
331
  @return: The (normalized) vector (L{Vector3d}).
350
332
  '''
351
333
  v = Vector3d.unit(self) if norm else self
352
- return Vector3d(v.x, v.y, v.z, name=self.name)
334
+ return Vector3d(v, name=self.name)
353
335
 
354
336
  @deprecated_method
355
337
  def to4xyzh(self, h=None): # PYCHOK no cover
@@ -373,10 +355,6 @@ class NvectorBase(Vector3d): # XXX kept private
373
355
  return self.xyz.to4Tuple(self.h)
374
356
 
375
357
 
376
- NorthPole = NvectorBase(0, 0, +1, name='NorthPole') # North pole (C{Nvector})
377
- SouthPole = NvectorBase(0, 0, -1, name='SouthPole') # South pole (C{Nvector})
378
-
379
-
380
358
  class _N_vector_(NvectorBase):
381
359
  '''(INTERNAL) Minimal, low-overhead C{n-vector}.
382
360
  '''
@@ -388,16 +366,19 @@ class _N_vector_(NvectorBase):
388
366
  self.name = name
389
367
 
390
368
 
369
+ NorthPole = _N_vector_(0, 0, +1, name='NorthPole') # North pole
370
+ SouthPole = _N_vector_(0, 0, -1, name='SouthPole') # South pole
371
+
372
+
391
373
  class LatLonNvectorBase(LatLonBase):
392
- '''(INTERNAL) Base class for n-vector-based ellipsoidal and
393
- spherical C{LatLon} classes.
374
+ '''(INTERNAL) Base class for n-vector-based ellipsoidal and spherical C{LatLon}s.
394
375
  '''
395
376
 
396
377
  def _update(self, updated, *attrs, **setters): # PYCHOK _Nv=None
397
378
  '''(INTERNAL) Zap cached attributes if updated.
398
379
 
399
- @see: C{ellipsoidalNvector.LatLon} and C{sphericalNvector.LatLon}
400
- for the special case of B{C{_Nv}}.
380
+ @see: C{ellipsoidalNvector.LatLon} and C{sphericalNvector.LatLon} for
381
+ the special case of B{C{_Nv}}.
401
382
  '''
402
383
  if updated:
403
384
  _Nv, setters = _xkwds_pop2(setters, _Nv=None)
@@ -498,10 +479,10 @@ class LatLonNvectorBase(LatLonBase):
498
479
  @raise ValueError: Some B{C{points}} coincide or invalid B{C{distance1}},
499
480
  B{C{distance2}}, B{C{distance3}} or B{C{radius}}.
500
481
 
501
- @see: U{Trilateration<https://WikiPedia.org/wiki/Trilateration>},
502
- Veness' JavaScript U{Trilateration<https://www.Movable-Type.co.UK/
503
- scripts/latlong-vectors.html>} and method C{LatLon.trilaterate5}
504
- of other, non-C{Nvector LatLon} classes.
482
+ @see: U{Trilateration<https://WikiPedia.org/wiki/Trilateration>}, I{Veness}'
483
+ JavaScript U{Trilateration<https://www.Movable-Type.co.UK/scripts/
484
+ latlong-vectors.html>} and method C{LatLon.trilaterate5} of other,
485
+ non-C{Nvector LatLon} classes.
505
486
  '''
506
487
  return _trilaterate(self, distance1, self.others(point2=point2), distance2,
507
488
  self.others(point3=point3), distance3,
@@ -510,16 +491,13 @@ class LatLonNvectorBase(LatLonBase):
510
491
 
511
492
  def trilaterate5(self, distance1, point2, distance2, point3, distance3, # PYCHOK signature
512
493
  area=False, eps=EPS1, radius=R_M, wrap=False):
513
- '''B{Not implemented} for C{B{area}=True} and falls back to method
514
- C{trilaterate} otherwise.
494
+ '''B{Not implemented} for C{B{area}=True} and falls back to method C{trilaterate}.
515
495
 
516
- @return: A L{Trilaterate5Tuple}C{(min, minPoint, max, maxPoint, n)}
517
- with a single trilaterated intersection C{minPoint I{is}
518
- maxPoint}, C{min I{is} max} the nearest intersection
519
- margin and count C{n = 1}.
496
+ @return: A L{Trilaterate5Tuple}C{(min, minPoint, max, maxPoint, n)} with a
497
+ single trilaterated intersection C{minPoint I{is} maxPoint}, C{min
498
+ I{is} max} the nearest intersection margin and count C{n = 1}.
520
499
 
521
- @raise NotImplementedError: Keyword argument C{B{area}=True} not
522
- (yet) supported.
500
+ @raise NotImplementedError: Keyword argument C{B{area}=True} not (yet) supported.
523
501
 
524
502
  @see: Method L{trilaterate} for other and more details.
525
503
  '''
@@ -540,6 +518,45 @@ class LatLonNvectorBase(LatLonBase):
540
518
  raise IntersectionError(area=area, eps=eps, radius=radius, wrap=wrap, txt=t)
541
519
 
542
520
 
521
+ def n_xyz2latlon(x_xyz, y=0, z=0, **name):
522
+ '''Convert C{n-vector} to (geodetic) lat- and longitude in C{degrees}.
523
+
524
+ @arg x_xyz: X component (C{scalar}) or (3-D) vector (C{Nvector},
525
+ L{Vector3d}, L{Vector3Tuple} or L{Vector4Tuple}).
526
+ @kwarg y: Y component of vector (C{scalar}), required if C{B{x_xyz} is
527
+ scalar} and same units as B{C{x_xyz}}, ignored otherwise.
528
+ @kwarg z: Z component of vector (C{scalar}), like B{C{y}}.
529
+ @kwarg name: Optional C{B{name}=NN} (C{str}).
530
+
531
+ @return: A L{LatLon2Tuple}C{(lat, lon)}.
532
+
533
+ @see: Function L{n_xyz2philam}.
534
+ '''
535
+ ll = map(degrees, n_xyz2philam(x_xyz, y, z))
536
+ return LatLon2Tuple(*ll, **name)
537
+
538
+
539
+ def n_xyz2philam(x_xyz, y=0, z=0, **name):
540
+ '''Convert C{n-vector} to (geodetic) lat- and longitude in C{radians}.
541
+
542
+ @arg x_xyz: X component (C{scalar}) or (3-D) vector (C{Nvector},
543
+ L{Vector3d}, L{Vector3Tuple} or L{Vector4Tuple}).
544
+ @kwarg y: Y component of vector (C{scalar}), required if C{B{x_xyz} is
545
+ scalar} and same units as B{C{x_xyz}}, ignored otherwise.
546
+ @kwarg z: Z component of vector (C{scalar}), like B{C{y}}.
547
+ @kwarg name: Optional C{B{name}=NN} (C{str}).
548
+
549
+ @return: A L{PhiLam2Tuple}C{(phi, lam)}.
550
+
551
+ @see: Function L{n_xyz2latlon}.
552
+ '''
553
+ try:
554
+ x, y, z = x_xyz.xyz
555
+ except AttributeError:
556
+ x = x_xyz
557
+ return PhiLam2Tuple(atan2(z, hypot(x, y)), atan2(y, x), **name)
558
+
559
+
543
560
  def _nsumOf(nvs, h_None, Vector, Vector_kwds): # .sphericalNvector, .vector3d
544
561
  '''(INTERNAL) Separated to allow callers to embellish exceptions.
545
562
  '''
@@ -670,7 +687,7 @@ __all__ += _ALL_DOCS(LatLonNvectorBase, NvectorBase, sumOf) # classes
670
687
 
671
688
  # **) MIT License
672
689
  #
673
- # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
690
+ # Copyright (C) 2016-2025 -- mrJean1 at Gmail -- All Rights Reserved.
674
691
  #
675
692
  # Permission is hereby granted, free of charge, to any person obtaining a
676
693
  # copy of this software and associated documentation files (the "Software"),
pygeodesy/osgr.py CHANGED
@@ -53,7 +53,7 @@ from pygeodesy.utily import degrees90, degrees180, sincostan3, truncate
53
53
  from math import cos, fabs, radians, sin, sqrt
54
54
 
55
55
  __all__ = _ALL_LAZY.osgr
56
- __version__ = '24.08.13'
56
+ __version__ = '24.11.06'
57
57
 
58
58
  _equivalent_ = 'equivalent'
59
59
  _OSGR_ = 'OSGR'
@@ -505,20 +505,17 @@ def _ll2LatLon3(ll, LatLon, datum, LatLon_kwds):
505
505
 
506
506
 
507
507
  def parseOSGR(strOSGR, Osgr=Osgr, **name_Osgr_kwds):
508
- '''Parse a string representing an OS Grid Reference, consisting
509
- of C{"[GD] easting northing"}.
508
+ '''Parse a string representing an OS Grid Reference, consisting of C{"[GD]
509
+ easting northing"}.
510
510
 
511
- Accepts standard OS Grid References like "SU 387 148", with
512
- or without whitespace separators, from 2- up to 22-digit
513
- references, or all-numeric, comma-separated references in
514
- meters, for example "438700,114800".
511
+ Accepts standard OS Grid References like "SU 387 148", with or without
512
+ whitespace separators, from 2- up to 22-digit references, or all-numeric,
513
+ comma-separated references in meters, for example "438700,114800".
515
514
 
516
515
  @arg strOSGR: An OSGR coordinate (C{str}).
517
- @kwarg Osgr: Optional class to return the OSGR coordinate
518
- (L{Osgr}) or C{None}.
519
- @kwarg name_Osgr_kwds: Optional C{B{name}=NN} (C{str}) and
520
- optional, additional B{C{Osgr}} keyword arguments,
521
- ignored if C{B{Osgr} is None}.
516
+ @kwarg Osgr: Optional class to return the OSGR coordinate (L{Osgr}) or C{None}.
517
+ @kwarg name_Osgr_kwds: Optional C{B{name}=NN} (C{str}) and optionally, additional
518
+ B{C{Osgr}} keyword arguments, ignored if C{B{Osgr} is None}.
522
519
 
523
520
  @return: An (B{C{Osgr}}) instance or if C{B{Osgr} is None}, an
524
521
  L{EasNor2Tuple}C{(easting, northing)}.
@@ -725,7 +722,7 @@ if __name__ == '__main__':
725
722
 
726
723
  # **) MIT License
727
724
  #
728
- # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
725
+ # Copyright (C) 2016-2025 -- mrJean1 at Gmail -- All Rights Reserved.
729
726
  #
730
727
  # Permission is hereby granted, free of charge, to any person obtaining a
731
728
  # copy of this software and associated documentation files (the "Software"),
pygeodesy/points.py CHANGED
@@ -1665,7 +1665,7 @@ __all__ += _ALL_DOCS(_Array2LatLon, _Basequence)
1665
1665
 
1666
1666
  # **) MIT License
1667
1667
  #
1668
- # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
1668
+ # Copyright (C) 2016-2025 -- mrJean1 at Gmail -- All Rights Reserved.
1669
1669
  #
1670
1670
  # Permission is hereby granted, free of charge, to any person obtaining a
1671
1671
  # copy of this software and associated documentation files (the "Software"),
pygeodesy/props.py CHANGED
@@ -26,7 +26,7 @@ from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, \
26
26
  from functools import wraps as _wraps
27
27
 
28
28
  __all__ = _ALL_LAZY.props
29
- __version__ = '24.10.19'
29
+ __version__ = '24.11.06'
30
30
 
31
31
  _class_ = 'class'
32
32
  _DNL_ = _NL_ * 2 # PYCHOK used!
@@ -369,7 +369,7 @@ class _property_RO___(_PropertyBase):
369
369
  '''
370
370
  _PropertyBase.__init__(self, method, self._fget, self._fset_error, doc=doc)
371
371
 
372
- def _fdel(self, unused): # PYCHOK no cover
372
+ def _fdel(self, *unused): # PYCHOK no cover
373
373
  '''Silently ignored, always.
374
374
  '''
375
375
  pass
@@ -701,7 +701,7 @@ _throwarning = DeprecationWarnings.throw
701
701
 
702
702
  # **) MIT License
703
703
  #
704
- # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
704
+ # Copyright (C) 2016-2025 -- mrJean1 at Gmail -- All Rights Reserved.
705
705
  #
706
706
  # Permission is hereby granted, free of charge, to any person obtaining a
707
707
  # copy of this software and associated documentation files (the "Software"),
pygeodesy/resections.py CHANGED
@@ -15,26 +15,26 @@ from __future__ import division as _; del _ # PYCHOK semicolon
15
15
  from pygeodesy.basics import map1, map2, _zip, _ALL_LAZY
16
16
  from pygeodesy.constants import EPS, EPS0, EPS02, INT0, PI, PI2, PI_2, PI_4, \
17
17
  _0_0, _0_5, _1_0, _N_1_0, _2_0, _N_2_0, _4_0, \
18
- _16_0, _180_0, _360_0, _copysign_0_0, isnear0, \
19
- _over, _umod_360
18
+ _16_0, _180_0, _360_0, isnear0, _over, _umod_360
20
19
  from pygeodesy.errors import _and, _or, TriangleError, _ValueError, _xcallable, \
21
20
  _xkwds, _xkwds_pop2
22
21
  from pygeodesy.fmath import favg, Fdot, fidw, fmean, hypot, hypot2_
23
22
  from pygeodesy.fsums import _Fsumf_, fsumf_, fsum1, fsum1f_
24
23
  from pygeodesy.interns import _a_, _A_, _area_, _b_, _B_, _c_, _C_, _coincident_, \
25
- _colinear_, _d_, _eps_, _invalid_, _negative_, _not_, \
24
+ _colinear_, _d_, _invalid_, _negative_, _not_, \
26
25
  _rIn_, _SPACE_
27
26
  # from pygeodesy.lazily import _ALL_LAZY # from .basics
28
27
  from pygeodesy.named import _NamedTuple, _Pass, Fmt
29
28
  # from pygeodesy.streprs import Fmt # from .named
30
29
  from pygeodesy.units import Degrees, Distance, Radians
31
- from pygeodesy.utily import acos1, asin1, sincos2, sincos2_, sincos2d, sincos2d_
30
+ from pygeodesy.utily import acos1, asin1, atan2, sincos2, sincos2_, \
31
+ sincos2d, sincos2d_
32
32
  from pygeodesy.vector3d import _otherV3d, Vector3d
33
33
 
34
- from math import cos, atan2, degrees, fabs, radians, sin, sqrt
34
+ from math import cos, degrees, fabs, radians, sin, sqrt
35
35
 
36
36
  __all__ = _ALL_LAZY.resections
37
- __version__ = '24.09.23'
37
+ __version__ = '24.11.27'
38
38
 
39
39
  _concyclic_ = 'concyclic'
40
40
  _PA_ = 'PA'
@@ -200,11 +200,11 @@ def cassini(pointA, pointB, pointC, alpha, beta, useZ=False, **Clas_and_kwds):
200
200
  alpha=alpha, beta=beta, cause=x)
201
201
 
202
202
 
203
- def _Clas(where, point, Clas_and_kwds, *args):
203
+ def _Clas(which, point, Clas_and_kwds, *args):
204
204
  '''(INTERNAL) Return a C{B{Clas}=point.classof} survey point.
205
205
  '''
206
206
  Clas, kwds = _xkwds_pop2(Clas_and_kwds, Clas=point.classof)
207
- return Clas(*args, **_xkwds(kwds, name=where.__name__))
207
+ return Clas(*args, **_xkwds(kwds, name=which.__name__))
208
208
 
209
209
 
210
210
  def collins5(pointA, pointB, pointC, alpha, beta, useZ=False, **Clas_and_kwds):
@@ -252,8 +252,8 @@ def collins5(pointA, pointB, pointC, alpha, beta, useZ=False, **Clas_and_kwds):
252
252
 
253
253
  def _xyz(d, r, A, B, C, useZ):
254
254
  s, c = sincos2(r)
255
- x = A.x + d * s
256
- y = A.y + d * c
255
+ x = d * s + A.x # fma(d, s, A.x)
256
+ y = d * c + A.y # fma(d, c, A.y)
257
257
  z = _zidw(x, y, useZ, A, B, C)
258
258
  return x, y, z
259
259
 
@@ -282,11 +282,10 @@ def collins5(pointA, pointB, pointC, alpha, beta, useZ=False, **Clas_and_kwds):
282
282
  d = b * sin(zb - zh) / sra # A.minus(P).length
283
283
  r = zh - ra # zb - PI + (PI - ra - (zb - zh))
284
284
  P = _xyz(d, r, A, B, C, useZ)
285
- P = _Clas(collins5, pointA, Clas_and_kwds, *P)
286
285
 
286
+ P = _Clas(collins5, pointA, Clas_and_kwds, *P)
287
287
  H = _Clas(collins5, pointA, Clas_and_kwds, *H)
288
288
  a = B.minus(C).length
289
-
290
289
  return Collins5Tuple(P, H, a, b, c, name=collins5.__name__)
291
290
 
292
291
  except (TypeError, ValueError) as x:
@@ -312,7 +311,7 @@ def pierlot(point1, point2, point3, alpha12, alpha23, useZ=False, eps=EPS,
312
311
  B{C{alpha3 - alpha2}}(C{degrees}).
313
312
  @kwarg useZ: If C{True}, interpolate the survey point's Z component,
314
313
  otherwise use C{z=INT0} (C{bool}).
315
- @kwarg eps: Tolerance for C{cot} (pseudo-)singularities (C{float}).
314
+ @kwarg eps: Tolerance for C{cot}angent (pseudo-)singularities (C{float}).
316
315
  @kwarg Clas_and_kwds: Optional class C{B{Clas}=B{point1}.classof} to
317
316
  return the survey point with optionally other B{C{Clas}}
318
317
  keyword arguments to instantiate the survey point.
@@ -341,7 +340,8 @@ def pierlot(point1, point2, point3, alpha12, alpha23, useZ=False, eps=EPS,
341
340
  def _cot(s, c): # -eps < I{approximate} cotangent < eps
342
341
  if eps > 0:
343
342
  return c / (min(s, -eps) if s < 0 else max(s, eps))
344
- raise ValueError(_SPACE_(_eps_, _not_, _positive_))
343
+ t = Fmt.PARENSPACED(eps=eps)
344
+ raise ValueError(_SPACE_(t, _not_, _positive_))
345
345
 
346
346
  B1, B2, B3 = _B3(useZ, point1, point2, point3)
347
347
  try:
@@ -353,7 +353,7 @@ def pierlot(point1, point2, point3, alpha12, alpha23, useZ=False, eps=EPS,
353
353
  alpha12=alpha12, alpha23=alpha23, eps=eps, cause=x)
354
354
 
355
355
 
356
- def _pierlot3(B1, B2, B3, a12, a23, useZ, cot):
356
+ def _pierlot3(B1, B2, B3, a12, a23, useZ, _cot):
357
357
  '''(INTERNAL) Shared L{pierlot} and L{pierlotx}.
358
358
  '''
359
359
  x1_, y1_, _ = B1.minus(B2).xyz3
@@ -365,14 +365,14 @@ def _pierlot3(B1, B2, B3, a12, a23, useZ, cot):
365
365
  # = (1 - (c12 * c23) / (s12 * s23)) / (c12 * s23 + s12 * c23) / (s12 * s23)
366
366
  # = (s12 * s23 - c12 * c23) / (c12 * s23 + s12 * c23)
367
367
  # = c31 / s31
368
- cot31 = cot(fsum1f_(c12 * s23, s12 * c23), # s31
369
- fsum1f_(s12 * s23, -c12 * c23)) # c31
368
+ cot31 = _cot(fsum1f_(c12 * s23, s12 * c23), # s31
369
+ fsum1f_(s12 * s23, -c12 * c23)) # c31
370
370
 
371
371
  K = _Fsumf_(x3_ * x1_, cot31 * (y3_ * x1_),
372
372
  y3_ * y1_, -cot31 * (x3_ * y1_))
373
373
  if K:
374
- cot12 = cot(s12, c12)
375
- cot23 = cot(s23, c23)
374
+ cot12 = _cot(s12, c12)
375
+ cot23 = _cot(s23, c23)
376
376
 
377
377
  # x12 = x1_ + cot12 * y1_
378
378
  # y12 = y1_ - cot12 * x1_
@@ -427,8 +427,8 @@ def pierlotx(point1, point2, point3, alpha1, alpha2, alpha3, useZ=False,
427
427
  return the survey point with optionally other B{C{Clas}}
428
428
  keyword arguments to instantiate the survey point.
429
429
 
430
- @return: The survey (or robot) point, an instance of B{C{Clas}} or B{C{point1}}'s
431
- (sub-)class.
430
+ @return: The survey (or robot) point, an instance of B{C{Clas}} or
431
+ B{C{point1}}'s (sub-)class.
432
432
 
433
433
  @raise ResectionError: Near-coincident, -colinear or -concyclic points or
434
434
  invalid B{C{alpha1}}, B{C{alpha2}} or B{C{alpha3}}.
@@ -450,7 +450,7 @@ def pierlotx(point1, point2, point3, alpha1, alpha2, alpha3, useZ=False,
450
450
 
451
451
  def _cot(s, c): # I{exact} cotangent
452
452
  try:
453
- return (c / s) if c else _copysign_0_0(s)
453
+ return (c / s) # if c else _copysign_0_0(s)
454
454
  except ZeroDivisionError:
455
455
  raise ValueError(_or(_coincident_, _colinear_))
456
456
 
@@ -466,7 +466,7 @@ def pierlotx(point1, point2, point3, alpha1, alpha2, alpha3, useZ=False,
466
466
  alpha1=alpha1, alpha2=alpha2, alpha3=alpha3, cause=x)
467
467
 
468
468
 
469
- def _pierlotx3(a_z_Bs, useZ, cot, Cs):
469
+ def _pierlotx3(a_z_Bs, useZ, _cot, Cs):
470
470
  '''(INTERNAL) Core of L{pierlotx}.
471
471
  '''
472
472
  (a12, z12, B1), \
@@ -482,14 +482,14 @@ def _pierlotx3(a_z_Bs, useZ, cot, Cs):
482
482
  a23, B2, B3 = a12, B3, B2
483
483
  else:
484
484
  Cs(4)
485
- return _pierlot3(B1, B2, B3, a12, a23, useZ, cot)
485
+ return _pierlot3(B1, B2, B3, a12, a23, useZ, _cot)
486
486
 
487
487
  x1_, y1_, _ = B1.minus(B3).xyz3
488
488
  x2_, y2_, _ = B2.minus(B3).xyz3
489
489
 
490
490
  K = _Fsumf_(y1_ * x2_, -x1_ * y2_)
491
491
  if K:
492
- cot23 = cot(*sincos2d(a23))
492
+ cot23 = _cot(*sincos2d(a23))
493
493
 
494
494
  # x23 = x2_ + cot23 * y2_
495
495
  # y23 = y2_ - cot23 * x2_
@@ -1019,7 +1019,7 @@ def _zidw(x, y, useZ, *ABC):
1019
1019
 
1020
1020
  # **) MIT License
1021
1021
  #
1022
- # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
1022
+ # Copyright (C) 2016-2025 -- mrJean1 at Gmail -- All Rights Reserved.
1023
1023
  #
1024
1024
  # Permission is hereby granted, free of charge, to any person obtaining a
1025
1025
  # copy of this software and associated documentation files (the "Software"),
@@ -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__ = '24.09.02'
12
+ __version__ = '24.11.07'
13
13
 
14
14
  if _unLazy0: # or _isfrozen
15
15
  from pygeodesy.rhumb.aux_ import RhumbAux, RhumbLineAux
@@ -25,7 +25,7 @@ else: # lazily import modules and exported attrs
25
25
 
26
26
  # **) MIT License
27
27
  #
28
- # Copyright (C) 2018-2024 -- mrJean1 at Gmail -- All Rights Reserved.
28
+ # Copyright (C) 2018-2025 -- mrJean1 at Gmail -- All Rights Reserved.
29
29
  #
30
30
  # Permission is hereby granted, free of charge, to any person obtaining a
31
31
  # copy of this software and associated documentation files (the "Software"),