pygeodesy 24.8.4__py2.py3-none-any.whl → 24.9.9__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 (76) hide show
  1. {PyGeodesy-24.8.4.dist-info → PyGeodesy-24.9.9.dist-info}/METADATA +17 -16
  2. PyGeodesy-24.9.9.dist-info/RECORD +118 -0
  3. {PyGeodesy-24.8.4.dist-info → PyGeodesy-24.9.9.dist-info}/WHEEL +1 -1
  4. pygeodesy/__init__.py +23 -23
  5. pygeodesy/__main__.py +46 -47
  6. pygeodesy/auxilats/_CX_4.py +104 -181
  7. pygeodesy/auxilats/_CX_6.py +152 -277
  8. pygeodesy/auxilats/_CX_8.py +211 -438
  9. pygeodesy/auxilats/_CX_Rs.py +222 -0
  10. pygeodesy/auxilats/__init__.py +2 -2
  11. pygeodesy/auxilats/__main__.py +30 -38
  12. pygeodesy/auxilats/auxDST.py +2 -2
  13. pygeodesy/auxilats/auxLat.py +28 -36
  14. pygeodesy/auxilats/auxily.py +30 -50
  15. pygeodesy/basics.py +18 -7
  16. pygeodesy/booleans.py +10 -11
  17. pygeodesy/cartesianBase.py +5 -5
  18. pygeodesy/constants.py +35 -34
  19. pygeodesy/ellipsoidalBase.py +18 -15
  20. pygeodesy/ellipsoidalExact.py +2 -2
  21. pygeodesy/ellipsoidalGeodSolve.py +2 -2
  22. pygeodesy/ellipsoidalKarney.py +2 -2
  23. pygeodesy/ellipsoidalNvector.py +2 -2
  24. pygeodesy/ellipsoidalVincenty.py +7 -6
  25. pygeodesy/elliptic.py +154 -88
  26. pygeodesy/epsg.py +3 -3
  27. pygeodesy/etm.py +71 -59
  28. pygeodesy/fmath.py +99 -90
  29. pygeodesy/fsums.py +201 -14
  30. pygeodesy/gars.py +9 -8
  31. pygeodesy/geodesici.py +6 -5
  32. pygeodesy/geodesicx/_C4_24.py +1 -3
  33. pygeodesy/geodesicx/_C4_27.py +1 -3
  34. pygeodesy/geodesicx/_C4_30.py +1 -3
  35. pygeodesy/geodesicx/__init__.py +1 -1
  36. pygeodesy/geodesicx/__main__.py +44 -46
  37. pygeodesy/geodesicx/gx.py +3 -3
  38. pygeodesy/geodesicx/gxarea.py +5 -5
  39. pygeodesy/geodesicx/gxbases.py +32 -18
  40. pygeodesy/geodsolve.py +3 -3
  41. pygeodesy/geohash.py +18 -11
  42. pygeodesy/geoids.py +293 -315
  43. pygeodesy/heights.py +150 -158
  44. pygeodesy/internals.py +70 -9
  45. pygeodesy/interns.py +4 -4
  46. pygeodesy/karney.py +83 -60
  47. pygeodesy/ktm.py +4 -4
  48. pygeodesy/latlonBase.py +13 -7
  49. pygeodesy/lazily.py +13 -8
  50. pygeodesy/ltp.py +5 -6
  51. pygeodesy/ltpTuples.py +7 -1
  52. pygeodesy/mgrs.py +47 -42
  53. pygeodesy/named.py +8 -4
  54. pygeodesy/namedTuples.py +14 -1
  55. pygeodesy/osgr.py +7 -7
  56. pygeodesy/points.py +2 -2
  57. pygeodesy/props.py +7 -6
  58. pygeodesy/resections.py +7 -7
  59. pygeodesy/rhumb/__init__.py +1 -1
  60. pygeodesy/rhumb/aux_.py +42 -60
  61. pygeodesy/rhumb/solve.py +3 -3
  62. pygeodesy/simplify.py +10 -10
  63. pygeodesy/sphericalBase.py +3 -3
  64. pygeodesy/sphericalTrigonometry.py +2 -2
  65. pygeodesy/streprs.py +3 -3
  66. pygeodesy/triaxials.py +207 -201
  67. pygeodesy/units.py +3 -3
  68. pygeodesy/unitsBase.py +4 -4
  69. pygeodesy/utmupsBase.py +3 -3
  70. pygeodesy/vector2d.py +158 -51
  71. pygeodesy/vector3d.py +13 -52
  72. pygeodesy/vector3dBase.py +81 -63
  73. pygeodesy/webmercator.py +3 -3
  74. pygeodesy/wgrs.py +20 -22
  75. PyGeodesy-24.8.4.dist-info/RECORD +0 -117
  76. {PyGeodesy-24.8.4.dist-info → PyGeodesy-24.9.9.dist-info}/top_level.txt +0 -0
pygeodesy/heights.py CHANGED
@@ -78,8 +78,8 @@ from pygeodesy.errors import _AssertionError, LenError, PointsError, \
78
78
  # cosineLaw, equirectangular4, euclidean, flatLocal, \
79
79
  # flatPolar, haversine, thomas, vincentys # _MODS.into
80
80
  # from pygeodesy.internals import _version2 # _MODS
81
- from pygeodesy.interns import NN, _COMMASPACE_, _cubic_, _insufficient_, _linear_, \
82
- _NOTEQUAL_, _PLUS_, _scipy_, _SPACE_, _STAR_
81
+ from pygeodesy.interns import NN, _COMMASPACE_, _insufficient_, _NOTEQUAL_, \
82
+ _PLUS_, _scipy_, _SPACE_, _STAR_
83
83
  from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS, _FOR_DOCS
84
84
  from pygeodesy.named import _name2__, _Named
85
85
  from pygeodesy.points import _distanceTo, LatLon_, Fmt, radians, _Wrap
@@ -91,12 +91,12 @@ from pygeodesy.units import _isDegrees, Float_, Int_
91
91
  # from math import radians # from .points
92
92
 
93
93
  __all__ = _ALL_LAZY.heights
94
- __version__ = '24.07.25'
94
+ __version__ = '24.08.24'
95
95
 
96
- _error_ = 'error'
97
- _formy = _MODS.into(formy=__name__)
98
- _llis_ = 'llis'
99
- _smoothing_ = 'smoothing'
96
+ _error_ = 'error'
97
+ _formy = _MODS.into(formy=__name__)
98
+ _linear_ = 'linear'
99
+ _llis_ = 'llis'
100
100
 
101
101
 
102
102
  class HeightError(PointsError):
@@ -144,26 +144,11 @@ def _as_llis2(llis, m=1, Error=HeightError): # in .geoids
144
144
  _as = _atuple # return tuple of interpolated heights
145
145
 
146
146
  if n < m:
147
- raise _insufficientError(m, Error=Error, llis=n)
147
+ raise _InsufficientError(m, Error=Error, llis=n)
148
148
  return _as, llis
149
149
 
150
150
 
151
- def _height_called(inst, lats, lons, Error=HeightError, **wrap): # in .geoids
152
- LLis, d = inst._LLis, inst.datum
153
- if _isDegrees(lats) and _isDegrees(lons):
154
- llis = LLis(lats, lons, datum=d)
155
- else:
156
- n, lats = len2(lats)
157
- m, lons = len2(lons)
158
- if n != m:
159
- # format a LenError, but raise an Error
160
- e = LenError(inst.__class__, lats=n, lons=m, txt=None)
161
- raise e if Error is LenError else Error(str(e))
162
- llis = [LLis(*t, datum=d) for t in zip(lats, lons)]
163
- return inst(llis, **wrap) # __call__(lli) or __call__(llis)
164
-
165
-
166
- def _insufficientError(need, Error=HeightError, **name_value): # PYCHOK no cover
151
+ def _InsufficientError(need, Error=HeightError, **name_value): # PYCHOK no cover
167
152
  # create an insufficient Error instance
168
153
  t = _COMMASPACE_(_insufficient_, str(need) + _PLUS_)
169
154
  return Error(txt=t, **name_value)
@@ -171,39 +156,48 @@ def _insufficientError(need, Error=HeightError, **name_value): # PYCHOK no cove
171
156
 
172
157
  def _orderedup(ts, lo=EPS, hi=PI2-EPS):
173
158
  # clip, order and remove duplicates
174
- # p = 0
175
- # for k in sorted(max(lo, min(hi, t)) for t in ts):
176
- # if k > p:
177
- # yield k
178
- # p = k
179
159
  return sorted(set(max(lo, min(hi, t)) for t in ts)) # list
180
160
 
181
161
 
182
- def _xyhs(wrap=False, **name_lls):
162
+ def _xyhs(wrap=False, _lat=_90_0, _lon=_180_0, **name_lls):
183
163
  # map (lat, lon, h) to (x, y, h) in radians, offset
184
164
  # x as 0 <= lon <= PI2 and y as 0 <= lat <= PI
185
165
  name, lls = _xkwds_item2(name_lls)
186
- _0, _90, _180 = _0_0, _90_0, _180_0
187
- _m, _r, _w = max, radians, _Wrap._latlonop(wrap)
166
+ _r, _w = radians, _Wrap._latlonop(wrap)
188
167
  try:
189
168
  for i, ll in enumerate(lls):
190
169
  y, x = _w(ll.lat, ll.lon)
191
- yield _m(_0, _r(x + _180)), \
192
- _m(_0, _r(y + _90)), ll.height
170
+ yield max(_0_0, _r(x + _lon)), \
171
+ max(_0_0, _r(y + _lat)), ll.height
193
172
  except Exception as e:
194
173
  i = Fmt.INDEX(name, i)
195
174
  raise HeightError(i, ll, cause=e)
196
175
 
197
176
 
198
- class _HeightBase(_Named): # in .geoids
177
+ class _HeightNamed(_Named): # in .geoids
199
178
  '''(INTERNAL) Interpolator base class.
200
179
  '''
201
180
  _datum = _WGS84 # default
181
+ _Error = HeightError
202
182
  _kmin = 2 # min number of knots
183
+
203
184
  _LLis = LatLon_ # ._height class
204
185
  _np_sp = None # (numpy, scipy)
205
186
  _wrap = None # wrap knots and llis
206
187
 
188
+ def _as_lls(self, lats, lons): # in .geoids
189
+ LLis, d = self._LLis, self.datum
190
+ if _isDegrees(lats) and _isDegrees(lons):
191
+ llis = LLis(lats, lons, datum=d)
192
+ else:
193
+ n, lats = len2(lats)
194
+ m, lons = len2(lons)
195
+ if n != m: # format a LenError, but raise self._Error
196
+ e = LenError(self.__class__, lats=n, lons=m, txt=None)
197
+ raise self._Error(str(e))
198
+ llis = [LLis(*t, datum=d) for t in zip(lats, lons)]
199
+ return llis
200
+
207
201
  @property_RO
208
202
  def datum(self):
209
203
  '''Get the C{datum} setting or the default (L{Datum}).
@@ -223,9 +217,14 @@ class _HeightBase(_Named): # in .geoids
223
217
  return self._wrap
224
218
 
225
219
 
226
- class _HeightsBase(_HeightBase): # in .geoids
220
+ class _HeightBase(_HeightNamed): # in .geoids
227
221
  '''(INTERNAL) Interpolator base class.
228
222
  '''
223
+ _k2interp2d = {-1: _linear_, # in .geoids._GeoidBase.__init__
224
+ -2: _linear_, # for backward compatibility
225
+ -3: 'cubic',
226
+ -5: 'quintic'}
227
+
229
228
  def __call__(self, *llis, **wrap): # PYCHOK no cover
230
229
  '''Interpolate the height for one or several locations. I{Must be overloaded}.
231
230
 
@@ -259,12 +258,29 @@ class _HeightsBase(_HeightBase): # in .geoids
259
258
  '''(INTERNAL) I{Must be overloaded}.'''
260
259
  self._notOverloaded(*args)
261
260
 
262
- def _eval(self, llis, **wrap): # XXX single arg, not *args
261
+ def _evalls(self, llis, **wrap): # XXX single arg, not *args
263
262
  _as, xis, yis, _ = self._as_xyllis4(llis, **wrap)
264
263
  try: # SciPy .ev signature: y first, then x!
265
264
  return _as(self._ev(yis, xis))
266
265
  except Exception as x:
267
- raise _SciPyIssue(x)
266
+ raise _SciPyIssue(x, self._ev_name)
267
+
268
+ def _ev2d(self, x, y): # PYCHOK no cover
269
+ '''(INTERNAL) I{Must be overloaded}.'''
270
+ self._notOverloaded(x, y)
271
+
272
+ @property_RO
273
+ def _ev_name(self):
274
+ '''(INTERNAL) Get the name of the C{.ev} method.
275
+ '''
276
+ _ev = str(self._ev)
277
+ if _scipy_ not in _ev:
278
+ _ev = str(self._ev2d)
279
+ # '<scipy.interpolate._interpolate.interp2d object at ...>
280
+ # '<function _HeightBase._interp2d.<locals>._bisplev at ...>
281
+ # '<bound method BivariateSpline.ev of ... object at ...>
282
+ _ev = _ev[1:].split(None, 4)
283
+ return Fmt.PAREN(_ev['sfb'.index(_ev[0][0])])
268
284
 
269
285
  def height(self, lats, lons, **wrap): # PYCHOK no cover
270
286
  '''Interpolate the height for one or several lat-/longitudes. I{Must be overloaded}.
@@ -285,7 +301,35 @@ class _HeightsBase(_HeightBase): # in .geoids
285
301
 
286
302
  @raise SciPyWarning: A C{scipy} warning as exception.
287
303
  '''
288
- self._notOverloaded(lats, lons, **wrap)
304
+ lls = self._as_lls(lats, lons)
305
+ return self(lls, **wrap) # __call__(ll) or __call__(lls)
306
+
307
+ def _interp2d(self, xs, ys, hs, kind=-3):
308
+ '''Create a C{scipy.interpolate.interp2d} or C{-.bisplrep/-ev}
309
+ interpolator before, respectively since C{SciPy} version 1.14.
310
+ '''
311
+ try:
312
+ spi = self.scipy_interpolate
313
+ if self._scipy_version() < (1, 14) and kind in self._k2interp2d:
314
+ # SciPy.interpolate.interp2d kind 'linear', 'cubic' or 'quintic'
315
+ # DEPRECATED since scipy 1.10, removed altogether in 1.14
316
+ self._ev2d = spi.interp2d(xs, ys, hs, kind=self._k2interp2d[kind])
317
+
318
+ else: # <https://scipy.GitHub.io/devdocs/tutorial/interpolate/interp_transition_guide.html>
319
+ k = self._kxky(abs(kind))
320
+ # spi.RectBivariateSpline needs strictly ordered xs and ys
321
+ r = spi.bisplrep(xs, ys, hs.T, kx=k, ky=k)
322
+
323
+ def _bisplev(x, y):
324
+ return spi.bisplev(x, y, r) # .T
325
+
326
+ self._ev2d = _bisplev
327
+
328
+ except Exception as x:
329
+ raise _SciPyIssue(x, self._ev_name)
330
+
331
+ def _kxky(self, kind):
332
+ return Int_(kind=kind, low=1, high=5, Error=self._Error)
289
333
 
290
334
  def _np_sp2(self, throwarnings=False): # PYCHOK no cover
291
335
  '''(INTERNAL) Import C{numpy} and C{scipy}, once.
@@ -329,85 +373,60 @@ class _HeightsBase(_HeightBase): # in .geoids
329
373
  xs, ys, hs = zip(*_xyhs(knots=knots, wrap=wrap)) # PYCHOK yield
330
374
  n = len(hs)
331
375
  if n < self.kmin:
332
- raise _insufficientError(self.kmin, knots=n)
376
+ raise _InsufficientError(self.kmin, knots=n)
333
377
  if name:
334
378
  self.name = name
335
379
  return map1(self.numpy.array, xs, ys, hs)
336
380
 
337
381
 
338
- class HeightCubic(_HeightsBase):
382
+ class HeightCubic(_HeightBase):
339
383
  '''Height interpolator based on C{SciPy} U{interp2d<https://docs.SciPy.org/
340
384
  doc/scipy/reference/generated/scipy.interpolate.interp2d.html>}
341
- C{kind='cubic'}.
385
+ C{kind='cubic'} or U{bisplrep/-ev<https://docs.SciPy.org/doc/scipy/
386
+ reference/generated/scipy.interpolate.interp2d.html>} C{kx=ky=3}.
342
387
  '''
343
- _interp2d = None
344
- _kind = _cubic_
345
- _kmin = 16
388
+ _kind = -3
389
+ _kmin = 16
346
390
 
347
391
  def __init__(self, knots, **name_wrap):
348
392
  '''New L{HeightCubic} interpolator.
349
393
 
350
394
  @arg knots: The points with known height (C{LatLon}s).
351
- @kwarg name_wrap: Optional C{B{name}=NN} for this height interpolator
352
- (C{str}) and keyword argument C{b{wrap}=False} to wrap or
353
- I{normalize} all B{C{knots}} and B{C{llis}} locations iff
354
- C{True} (C{bool}).
395
+ @kwarg name_wrap: Optional C{B{name}=NN} for this height interpolator (C{str})
396
+ and keyword argument C{b{wrap}=False} to wrap or I{normalize} all
397
+ B{C{knots}} and B{C{llis}} locations iff C{True} (C{bool}).
355
398
 
356
- @raise HeightError: Insufficient number of B{C{knots}} or
357
- invalid B{C{knot}}.
399
+ @raise HeightError: Insufficient number of B{C{knots}} or invalid B{C{knot}}.
358
400
 
359
- @raise ImportError: Package C{numpy} or C{scipy} not found
360
- or not installed.
401
+ @raise ImportError: Package C{numpy} or C{scipy} not found or not installed.
361
402
 
362
- @raise SciPyError: A C{scipy.interpolate.interp2d} issue.
403
+ @raise SciPyError: A C{scipy} issue.
363
404
 
364
- @raise SciPyWarning: A C{scipy.interpolate.interp2d} warning
365
- as exception.
405
+ @raise SciPyWarning: A C{scipy} warning as exception.
366
406
  '''
367
- spi = self.scipy_interpolate
368
-
369
- xs, ys, hs = self._xyhs3(knots, **name_wrap)
370
- try: # SciPy.interpolate.interp2d kind 'linear' or 'cubic'
371
- self._interp2d = spi.interp2d(xs, ys, hs, kind=self._kind)
372
- except Exception as x:
373
- raise _SciPyIssue(x)
407
+ xs_yx_hs = self._xyhs3(knots, **name_wrap)
408
+ self._interp2d(*xs_yx_hs, kind=self._kind)
374
409
 
375
410
  def __call__(self, *llis, **wrap):
376
411
  '''Interpolate the height for one or several locations.
377
412
 
378
- @raise SciPyError: A C{scipy.interpolate.interp2d} issue.
379
-
380
- @raise SciPyWarning: A C{scipy.interpolate.interp2d} warning
381
- as exception.
382
-
383
- @see: L{Here<_HeightsBase.__call__>} for further details.
413
+ @see: L{Here<_HeightBase.__call__>} for further details.
384
414
  '''
385
- return _HeightsBase._eval(self, llis, **wrap)
415
+ return self._evalls(llis, **wrap)
386
416
 
387
- def _ev(self, yis, xis): # PYCHOK expected
388
- # to make SciPy .interp2d signature(x, y), single (x, y)
417
+ def _ev(self, yis, xis): # PYCHOK overwritten with .RectBivariateSpline.ev
418
+ # to make SciPy .interp2d single (x, y) signature
389
419
  # match SciPy .ev signature(ys, xs), flipped multiples
390
- return map(self._interp2d, xis, yis)
391
-
392
- def height(self, lats, lons, **wrap):
393
- '''Interpolate the height for one or several lat-/longitudes.
394
-
395
- @raise SciPyError: A C{scipy.interpolate.interp2d} issue.
396
-
397
- @raise SciPyWarning: A C{scipy.interpolate.interp2d} warning
398
- as exception.
399
-
400
- @see: L{Here<_HeightsBase.height>} for further details.
401
- '''
402
- return _height_called(self, lats, lons, **wrap)
420
+ return map(self._ev2d, xis, yis)
403
421
 
404
422
 
405
423
  class HeightLinear(HeightCubic):
406
424
  '''Height interpolator based on C{SciPy} U{interp2d<https://docs.SciPy.org/
407
425
  doc/scipy/reference/generated/scipy.interpolate.interp2d.html>}
408
- C{kind='linear'}.
426
+ C{kind='linear'} or U{bisplrep/-ev<https://docs.SciPy.org/doc/scipy/
427
+ reference/generated/scipy.interpolate.interp2d.html>} C{kx=ky=1}.
409
428
  '''
410
- _kind = _linear_
429
+ _kind = -1
411
430
  _kmin = 2
412
431
 
413
432
  def __init__(self, knots, **name_wrap):
@@ -422,7 +441,7 @@ class HeightLinear(HeightCubic):
422
441
  height = HeightCubic.height
423
442
 
424
443
 
425
- class HeightLSQBiSpline(_HeightsBase):
444
+ class HeightLSQBiSpline(_HeightBase):
426
445
  '''Height interpolator using C{SciPy} U{LSQSphereBivariateSpline
427
446
  <https://docs.SciPy.org/doc/scipy/reference/generated/scipy.
428
447
  interpolate.LSQSphereBivariateSpline.html>}.
@@ -446,13 +465,12 @@ class HeightLSQBiSpline(_HeightsBase):
446
465
 
447
466
  @raise LenError: Unequal number of B{C{knots}} and B{C{weight}}s.
448
467
 
449
- @raise ImportError: Package C{numpy} or C{scipy} not found or
450
- not installed.
468
+ @raise ImportError: Package C{numpy} or C{scipy} not found or not
469
+ installed.
451
470
 
452
- @raise SciPyError: A C{scipy} C{LSQSphereBivariateSpline} issue.
471
+ @raise SciPyError: A C{scipy} issue.
453
472
 
454
- @raise SciPyWarning: A C{scipy} C{LSQSphereBivariateSpline}
455
- warning as exception.
473
+ @raise SciPyWarning: A C{scipy} warning as exception.
456
474
  '''
457
475
  np = self.numpy
458
476
  spi = self.scipy_interpolate
@@ -465,7 +483,7 @@ class HeightLSQBiSpline(_HeightsBase):
465
483
  w = float(w)
466
484
  if w <= 0:
467
485
  raise HeightError(weight=w)
468
- w = [w] * n
486
+ w = (w,) * n
469
487
  elif w is not None:
470
488
  m, w = len2(w)
471
489
  if m != n:
@@ -483,34 +501,17 @@ class HeightLSQBiSpline(_HeightsBase):
483
501
  self._ev = spi.LSQSphereBivariateSpline(ys, xs, hs,
484
502
  ts, ps, eps=EPS, w=w).ev
485
503
  except Exception as x:
486
- raise _SciPyIssue(x)
504
+ raise _SciPyIssue(x, self._ev_name)
487
505
 
488
506
  def __call__(self, *llis, **wrap):
489
507
  '''Interpolate the height for one or several locations.
490
508
 
491
- @raise SciPyError: A C{scipy} C{LSQSphereBivariateSpline} issue.
492
-
493
- @raise SciPyWarning: A C{scipy} C{LSQSphereBivariateSpline}
494
- warning as exception.
495
-
496
- @see: L{Here<_HeightsBase.__call__>} for further details.
509
+ @see: L{Here<_HeightBase.__call__>} for further details.
497
510
  '''
498
- return _HeightsBase._eval(self, llis, **wrap)
499
-
500
- def height(self, lats, lons, **wrap):
501
- '''Interpolate the height for one or several lat-/longitudes.
502
-
503
- @raise SciPyError: A C{scipy} C{LSQSphereBivariateSpline} issue.
511
+ return self._evalls(llis, **wrap)
504
512
 
505
- @raise SciPyWarning: A C{scipy} C{LSQSphereBivariateSpline}
506
- warning as exception.
507
513
 
508
- @see: L{Here<_HeightsBase.height>} for further details.
509
- '''
510
- return _height_called(self, lats, lons, **wrap)
511
-
512
-
513
- class HeightSmoothBiSpline(_HeightsBase):
514
+ class HeightSmoothBiSpline(_HeightBase):
514
515
  '''Height interpolator using C{SciPy} U{SmoothSphereBivariateSpline
515
516
  <https://docs.SciPy.org/doc/scipy/reference/generated/scipy.
516
517
  interpolate.SmoothSphereBivariateSpline.html>}.
@@ -527,54 +528,36 @@ class HeightSmoothBiSpline(_HeightsBase):
527
528
  I{normalize} all B{C{knots}} and B{C{llis}} locations iff
528
529
  C{True} (C{bool}).
529
530
 
530
- @raise HeightError: Insufficient number of B{C{knots}} or
531
- an invalid B{C{knot}} or B{C{s}}.
531
+ @raise HeightError: Insufficient number of B{C{knots}} or an invalid
532
+ B{C{knot}} or B{C{s}}.
532
533
 
533
534
  @raise ImportError: Package C{numpy} or C{scipy} not found or not
534
535
  installed.
535
536
 
536
- @raise SciPyError: A C{scipy} C{SmoothSphereBivariateSpline} issue.
537
+ @raise SciPyError: A C{scipy} issue.
537
538
 
538
- @raise SciPyWarning: A C{scipy} C{SmoothSphereBivariateSpline}
539
- warning as exception.
539
+ @raise SciPyWarning: A C{scipy} warning as exception.
540
540
  '''
541
541
  spi = self.scipy_interpolate
542
542
 
543
- s = Float_(s, name=_smoothing_, Error=HeightError, low=4)
543
+ s = Float_(smoothing=s, Error=HeightError, low=4)
544
544
 
545
545
  xs, ys, hs = self._xyhs3(knots, **name_wrap)
546
546
  try:
547
547
  self._ev = spi.SmoothSphereBivariateSpline(ys, xs, hs,
548
548
  eps=EPS, s=s).ev
549
549
  except Exception as x:
550
- raise _SciPyIssue(x)
550
+ raise _SciPyIssue(x, self._ev_name)
551
551
 
552
552
  def __call__(self, *llis, **wrap):
553
553
  '''Interpolate the height for one or several locations.
554
554
 
555
- @raise SciPyError: A C{scipy} C{SmoothSphereBivariateSpline} issue.
556
-
557
- @raise SciPyWarning: A C{scipy} C{SmoothSphereBivariateSpline}
558
- warning as exception.
559
-
560
- @see: L{Here<_HeightsBase.__call__>} for further details.
561
- '''
562
- return _HeightsBase._eval(self, llis, **wrap)
563
-
564
- def height(self, lats, lons, **wrap):
565
- '''Interpolate the height for one or several lat-/longitudes.
566
-
567
- @raise SciPyError: A C{scipy} C{SmoothSphereBivariateSpline} issue.
568
-
569
- @raise SciPyWarning: A C{scipy} C{SmoothSphereBivariateSpline}
570
- warning as exception.
571
-
572
- @see: L{Here<_HeightsBase.height>} for further details.
555
+ @see: L{Here<_HeightBase.__call__>} for further details.
573
556
  '''
574
- return _height_called(self, lats, lons, **wrap)
557
+ return self._evalls(llis, **wrap)
575
558
 
576
559
 
577
- class _HeightIDW(_HeightBase):
560
+ class _HeightIDW(_HeightNamed):
578
561
  '''(INTERNAL) Base class for U{Inverse Distance Weighting
579
562
  <https://WikiPedia.org/wiki/Inverse_distance_weighting>} (IDW) height
580
563
  interpolators.
@@ -608,7 +591,7 @@ class _HeightIDW(_HeightBase):
608
591
 
609
592
  n, self._knots = len2(knots)
610
593
  if n < self.kmin:
611
- raise _insufficientError(self.kmin, knots=n)
594
+ raise _InsufficientError(self.kmin, knots=n)
612
595
  self.beta = beta
613
596
  self._kwds = kwds or {}
614
597
 
@@ -684,6 +667,16 @@ class _HeightIDW(_HeightBase):
684
667
  i = Fmt.INDEX(knots=i)
685
668
  raise HeightError(i, k, cause=e)
686
669
 
670
+ def _distancesTo(self, _To):
671
+ '''(INTERNAL) Yield distances C{_To}.
672
+ '''
673
+ try:
674
+ for i, k in enumerate(self._knots):
675
+ yield _To(k)
676
+ except Exception as e:
677
+ i = Fmt.INDEX(knots=i)
678
+ raise HeightError(i, k, cause=e)
679
+
687
680
  def height(self, lats, lons, **wrap):
688
681
  '''Interpolate the height for one or several lat-/longitudes.
689
682
 
@@ -698,7 +691,8 @@ class _HeightIDW(_HeightBase):
698
691
  @raise HeightError: Insufficient or non-matching number of B{C{lats}}
699
692
  and B{C{lons}} or a L{pygeodesy.fidw} issue.
700
693
  '''
701
- return _height_called(self, lats, lons, **wrap)
694
+ lls = self._as_lls(lats, lons)
695
+ return self(lls, **wrap) # __call__(ll) or __call__(lls)
702
696
 
703
697
  @Property_RO
704
698
  def _heights(self):
@@ -851,7 +845,7 @@ class HeightIDWdistanceTo(_HeightIDW):
851
845
  _HeightIDW.__init__(self, knots, beta=beta, **name__distanceTo_kwds)
852
846
  ks0 = _distanceTo(HeightError, knots=self._knots)[0]
853
847
  # use knots[0] class and datum to create compatible points
854
- # in _height_called instead of class LatLon_ and datum None
848
+ # in ._as_lls instead of class LatLon_ and datum None
855
849
  self._datum = ks0.datum
856
850
  self._LLis = ks0.classof # type(ks0)
857
851
 
@@ -859,12 +853,11 @@ class HeightIDWdistanceTo(_HeightIDW):
859
853
  '''(INTERNAL) Yield distances to C{(x, y)}.
860
854
  '''
861
855
  kwds, ll = self._kwds, self._LLis(y, x)
862
- try:
863
- for i, k in enumerate(self._knots):
864
- yield k.distanceTo(ll, **kwds)
865
- except Exception as e:
866
- i = Fmt.INDEX(knots=i)
867
- raise HeightError(i, k, cause=e)
856
+
857
+ def _To(k):
858
+ return k.distanceTo(ll, **kwds)
859
+
860
+ return self._distancesTo(_To)
868
861
 
869
862
  if _FOR_DOCS:
870
863
  __call__ = _HeightIDW.__call__
@@ -892,12 +885,11 @@ class HeightIDWequirectangular(_HeightIDW):
892
885
  '''(INTERNAL) Yield distances to C{(x, y)}.
893
886
  '''
894
887
  _f, kwds = _formy.equirectangular4, self._kwds
895
- try:
896
- for i, k in enumerate(self._knots):
897
- yield _f(y, x, k.lat, k.lon, **kwds).distance2
898
- except Exception as e:
899
- i = Fmt.INDEX(knots=i)
900
- raise HeightError(i, k, cause=e)
888
+
889
+ def _To(k):
890
+ return _f(y, x, k.lat, k.lon, **kwds).distance2
891
+
892
+ return self._distancesTo(_To)
901
893
 
902
894
  if _FOR_DOCS:
903
895
  __call__ = _HeightIDW.__call__
@@ -1114,7 +1106,7 @@ class HeightIDWvincentys(_HeightIDW):
1114
1106
  height = _HeightIDW.height
1115
1107
 
1116
1108
 
1117
- __all__ += _ALL_DOCS(_HeightBase, _HeightIDW, _HeightsBase)
1109
+ __all__ += _ALL_DOCS(_HeightBase, _HeightIDW, _HeightNamed)
1118
1110
 
1119
1111
  # **) MIT License
1120
1112
  #
pygeodesy/internals.py CHANGED
@@ -18,6 +18,7 @@ _0_0 = 0.0 # PYCHOK in .basics, .constants
18
18
  _arm64_ = 'arm64'
19
19
  _iOS_ = 'iOS'
20
20
  _macOS_ = 'macOS'
21
+ _SIsecs = 'fs', 'ps', 'ns', 'us', 'ms', 'sec' # reversed
21
22
  _Windows_ = 'Windows'
22
23
 
23
24
 
@@ -206,6 +207,13 @@ class _MODS_Base(object):
206
207
  l3.insert(0, pypy)
207
208
  return l3
208
209
 
210
+ @_Property_RO
211
+ def _Str_Bytes(self):
212
+ '''Get all C{str} and C{bytes} types.
213
+ '''
214
+ import pygeodesy.basics as m
215
+ return m._Strs + m._Bytes # + (range, map)
216
+
209
217
  @_Property_RO
210
218
  def streprs(self):
211
219
  '''Get module C{pygeodesy.streprs}, I{once}.
@@ -253,6 +261,12 @@ def _enquote(strs, quote=_QUOTE2_, white=NN): # in .basics, .solveBase
253
261
  return strs
254
262
 
255
263
 
264
+ def _fper(p, q, per=100.0, prec=1):
265
+ '''Format a percentage C{B{p} * B{per} / B{q}} (C{str}).
266
+ '''
267
+ return '%.*f%%' % (prec, (float(p) * per / float(q)))
268
+
269
+
256
270
  def _headof(name):
257
271
  '''(INTERNAL) Get the head name of qualified C{name} or the C{name}.
258
272
  '''
@@ -291,6 +305,13 @@ def _isNix(): # in test/bases.py
291
305
  return _MODS.nix2[0]
292
306
 
293
307
 
308
+ def _isPyChecker():
309
+ '''(INTERNAL) Is C{PyChecker} running? (C{bool}).
310
+ '''
311
+ # .../pychecker/checker.py --limit 0 --stdlib pygeodesy/<mod>/<name>.py
312
+ return _sys.argv[0].endswith('/pychecker/checker.py')
313
+
314
+
294
315
  def _isPyPy(): # in test/bases.py
295
316
  '''(INTERNAL) Is this C{PyPy}? (C{bool})
296
317
  '''
@@ -342,12 +363,30 @@ def machine():
342
363
  return _MODS.bits_machine2[1]
343
364
 
344
365
 
366
+ def _Math_K_2():
367
+ '''(INTERNAL) Return the I{Karney} Math setting.
368
+ '''
369
+ return _MODS.karney._wrapped.Math_K_2
370
+
371
+
345
372
  def _name_version(pkg):
346
373
  '''(INTERNAL) Return C{pskg.__name__ + ' ' + .__version__}.
347
374
  '''
348
375
  return _SPACE_(pkg.__name__, pkg.__version__)
349
376
 
350
377
 
378
+ def _name_binary(path):
379
+ '''(INTERNAL) Return C{(basename + ' ' + version)} of an executable.
380
+ '''
381
+ if path:
382
+ try:
383
+ _, r = _MODS.solveBase._popen2((path, '--version'))
384
+ return _SPACE_(_os_path.basename(path), r.split()[-1])
385
+ except (IndexError, IOError, OSError):
386
+ pass
387
+ return NN
388
+
389
+
351
390
  def _osversion2(sep=NN): # in .lazily, test/bases.versions
352
391
  '''(INTERNAL) Get the O/S name and release as C{2-list} or C{str}.
353
392
  '''
@@ -443,15 +482,36 @@ def _Pythonarchine(sep=NN): # in .lazily, test/bases.py versions
443
482
  return sep.join(l3) if sep else l3 # 3- or 4-list
444
483
 
445
484
 
446
- def _sizeof(obj):
485
+ def _secs2str(secs): # in .geoids, ../test/bases.py
486
+ '''Convert a time in C{secs} to C{str}.
487
+ '''
488
+ if secs < _MODS.constants._100_0:
489
+ unit = len(_SIsecs) - 1
490
+ while 0 < secs < 1 and unit > 0:
491
+ secs *= 1e3 # _1000_0
492
+ unit -= 1
493
+ t = '%.3f %s' % (secs, _SIsecs[unit])
494
+ else:
495
+ m, s = divmod(secs, 60)
496
+ if m < 60:
497
+ t = '%d:%06.3f' % (int(m), s)
498
+ else:
499
+ h, m = divmod(int(m), 60)
500
+ t = '%d:%02d:%06.3f' % (h, m, s)
501
+ return t
502
+
503
+
504
+ def _sizeof(obj, deep=True):
447
505
  '''(INTERNAL) Recursively size an C{obj}ect.
448
506
 
449
- @return: The C{obj} size in bytes (C{int}),
450
- ignoring class attributes and
451
- counting duplicates only once or
452
- C{None}.
507
+ @kwarg deep: If C{True}, include the size of all
508
+ C{.__dict__.values()} (C{bool}).
453
509
 
454
- @note: With C{PyPy}, the size is always C{None}.
510
+ @return: The C{obj} size in bytes (C{int}), ignoring
511
+ class attributes and counting instances only
512
+ once or C{None}.
513
+
514
+ @note: With C{PyPy}, the returned size is always C{None}.
455
515
  '''
456
516
  try:
457
517
  _zB = _sys.getsizeof
@@ -471,9 +531,10 @@ def _sizeof(obj):
471
531
  if isinstance(o, dict):
472
532
  z += _zR(s, o.keys())
473
533
  z += _zR(s, o.values())
474
- elif _isiterablen(o): # not map, ...
534
+ elif _isiterablen(o) and not \
535
+ isinstance(o, _MODS._Str_Bytes):
475
536
  z += _zR(s, o)
476
- else:
537
+ elif deep:
477
538
  try: # size instance' attr values only
478
539
  z += _zR(s, o.__dict__.values())
479
540
  except AttributeError: # None, int, etc.
@@ -591,7 +652,7 @@ def _versions(sep=_SPACE_):
591
652
 
592
653
 
593
654
  __all__ = tuple(map(_dunder_nameof, (machine, print_, printf)))
594
- __version__ = '24.08.01'
655
+ __version__ = '24.09.04'
595
656
 
596
657
  if _dunder_ismain(__name__): # PYCHOK no cover
597
658