pygeodesy 24.7.7__py2.py3-none-any.whl → 24.7.24__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pygeodesy/karney.py CHANGED
@@ -4,14 +4,12 @@
4
4
  u'''Wrapper around several C{geomath.Math} functions from I{Karney}'s Python package U{geographiclib
5
5
  <https://PyPI.org/project/geographiclib>}, provided that package is installed.
6
6
 
7
- The I{wrapped} class methods return a L{GDict} instance offering access to the C{dict} items
8
- either by C{key} or by C{attribute} name.
9
- All methods of the I{wrapped} L{Geodesic<geodesicw.Geodesic>} and L{GeodesicLine<geodesicw.GeodesicLine>}
7
+ Methods of the I{wrapped} L{Geodesic<geodesicw.Geodesic>} and L{GeodesicLine<geodesicw.GeodesicLine>}
10
8
  classes return a L{GDict} instance offering access to the C{dict} items either by C{key} or by
11
9
  C{attribute} name.
12
10
 
13
- With env variable C{PYGEODESY_GEOGRAPHICLIB} left undefined or set to C{"2"}, modules L{geodesicx},
14
- L{geodesicw} and this module will use U{GeographicLib 2.0<https://GeographicLib.SourceForge.io/C++/doc/>}
11
+ With env variable C{PYGEODESY_GEOGRAPHICLIB} left undefined or set to C{"2"}, modules L{geodesicw},
12
+ L{geodesicx} and this module will use U{GeographicLib 2.0+<https://GeographicLib.SourceForge.io/C++/doc/>}
15
13
  and newer transcoding, otherwise C{1.52} or older.
16
14
 
17
15
  Karney-based functionality
@@ -60,8 +58,11 @@ Karney-based functionality
60
58
  - L{GnomonicExact}, L{GnomonicGeodSolve}, L{GnomonicKarney} -- U{Gnomonic
61
59
  <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Gnomonic.html>}
62
60
 
61
+ - L{Intersector} -- U{Intersect
62
+ <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Intersect.html>} from I{GeographicLib 2.3+}
63
+
63
64
  - L{JacobiConformal} -- U{JacobiConformal
64
- <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1JacobiConformal.html#details>}
65
+ <https://geographiclib.sourceforge.io/C++/doc/classGeographicLib_1_1experimental_1_1JacobiConformal.html>}
65
66
 
66
67
  - L{KTransverseMercator} - U{TransverseMercator
67
68
  <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1TransverseMercator.html>}
@@ -94,13 +95,14 @@ are I{transcoded} from C++ classes in I{Karney}'s U{GeographicLib<https://Geogra
94
95
 
95
96
  2. These C{pygeodesy} modules and classes
96
97
 
97
- - L{ellipsoidalGeodSolve}, L{ellipsoidalKarney}, L{geodsolve}, L{karney}, L{rhumb.solve}
98
+ - L{ellipsoidalGeodSolve}, L{ellipsoidalKarney}, L{geodesici}, L{geodsolve}, L{karney}, L{rhumb.solve}
98
99
  - L{EquidistantKarney}, L{FrechetKarney}, L{GeodesicSolve}, L{GeodesicLineSolve}, L{GnomonicGeodSolve},
99
- L{GnomonicKarney}, L{HeightIDWkarney}
100
+ L{GnomonicKarney}, L{HeightIDWkarney}, L{Intersectool}
100
101
 
101
- are or use I{wrappers} around I{Karney}'s Python U{geographiclib<https://PyPI.org/project/geographiclib>}
102
- C{geodesic}, C++ utility U{GeodSolve<https://GeographicLib.SourceForge.io/C++/doc/GeodSolve.1.html>} or
103
- C++ utility U{RhumbSolve<https://GeographicLib.SourceForge.io/C++/doc/RhumbSolve.1.html>}.
102
+ are or use I{wrappers} around I{Karney}'s Python U{geographiclib<https://PyPI.org/project/geographiclib>} or
103
+ C++ utility U{GeodSolve<https://GeographicLib.SourceForge.io/C++/doc/GeodSolve.1.html>},
104
+ U{IntersectTool<https://GeographicLib.SourceForge.io/C++/doc/IntersectTool.1.html>} or
105
+ U{RhumbSolve<https://GeographicLib.SourceForge.io/C++/doc/RhumbSolve.1.html>}.
104
106
 
105
107
  3. All C{pygeodesy} functions and methods to compute I{ellipsoidal} intersections, nearest points and trilaterations
106
108
 
@@ -139,12 +141,11 @@ in C{pygeodesy} are based on I{Karney}'s post U{Area of a spherical polygon
139
141
  # make sure int/int division yields float quotient, see .basics
140
142
  from __future__ import division as _; del _ # PYCHOK semicolon
141
143
 
142
- from pygeodesy.basics import _copysign, int1s, isint, itemsorted, neg, unsigned0, \
143
- _xgeographiclib, _zip, _version_info
144
+ from pygeodesy.basics import _copysign, isint, neg, unsigned0, _xgeographiclib, \
145
+ _zip, _version_info
144
146
  from pygeodesy.constants import NAN, _isfinite as _math_isfinite, _0_0, \
145
147
  _1_16th, _1_0, _2_0, _180_0, _N_180_0, _360_0
146
- from pygeodesy.errors import GeodesicError, _ValueError, _xkwds, _xkwds_get1, \
147
- _xkwds_kwds, _xkwds_not
148
+ from pygeodesy.errors import GeodesicError, _ValueError, _xkwds, _xkwds_get1
148
149
  from pygeodesy.fmath import cbrt, fremainder, norm2
149
150
  # from pygeodesy.internals import _version_info # from .basics
150
151
  from pygeodesy.interns import NN, _2_, _a12_, _area_, _azi1_, _azi2_, _azi12_, \
@@ -153,7 +154,7 @@ from pygeodesy.interns import NN, _2_, _a12_, _area_, _azi1_, _azi2_, _azi12_, \
153
154
  _UNDER_, _X_, _BAR_ # PYCHOK used!
154
155
  from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS, _getenv
155
156
  from pygeodesy.named import ADict, _NamedBase, _NamedTuple, notImplemented, _Pass
156
- from pygeodesy.props import deprecated_method, Property_RO
157
+ from pygeodesy.props import deprecated_method, Property_RO, property_ROnce
157
158
  from pygeodesy.units import Bearing as _Azi, Degrees as _Deg, Lat, Lon, \
158
159
  Meter as _M, Meter2 as _M2, Number_
159
160
  from pygeodesy.utily import atan2d, sincos2d, tand, _unrollon, fabs
@@ -161,7 +162,7 @@ from pygeodesy.utily import atan2d, sincos2d, tand, _unrollon, fabs
161
162
  # from math import fabs # from .utily
162
163
 
163
164
  __all__ = _ALL_LAZY.karney
164
- __version__ = '24.07.09'
165
+ __version__ = '24.07.16'
165
166
 
166
167
  _K_2_0 = _getenv('PYGEODESY_GEOGRAPHICLIB', _2_) == _2_
167
168
  _perimeter_ = 'perimeter'
@@ -208,39 +209,39 @@ class Area3Tuple(_NamedTuple): # in .geodesicx.gxarea
208
209
  _Units_ = ( Number_, _M, _M2)
209
210
 
210
211
 
211
- class Caps(object): # PYCHOK
212
+ class Caps(object):
212
213
  '''(INTERNAL) Overriden by C{Caps} below.
213
214
  '''
214
- EMPTY = 0 # formerly aka NONE
215
- _CAP_1 = 1 << 0 # for goedesici/-w
216
- _CAP_1p = 1 << 1 # for goedesici/-w
217
- _CAP_2 = 1 << 2
218
- _CAP_3 = 1 << 3 # for goedesici/-w
219
- # _CAP_4 = 1 << 4
220
- # _CAP_ALL = 0x1F
221
- # _CAP_MASK = _CAP_ALL
222
- LATITUDE = 1 << 7 # compute latitude C{lat2}
223
- LONGITUDE = 1 << 8 # compute longitude C{lon2} _CAP_3
224
- AZIMUTH = 1 << 9 # azimuths C{azi1} and C{azi2}
225
- DISTANCE = 1 << 10 # compute distance C{s12} _CAP_1
226
- DISTANCE_IN = 1 << 11 # allow distance C{s12} in Direct _CAP_1 | _CAP_1p
227
- REDUCEDLENGTH = 1 << 12 # compute reduced length C{m12} _CAP_1 | _CAP_2
228
- GEODESICSCALE = 1 << 13 # compute geodesic scales C{M12} and C{M21} _CAP_1 | _CAP_2
229
- AREA = 1 << 14 # compute area C{S12} _CAP_4
230
-
231
- STANDARD = AZIMUTH | DISTANCE | DISTANCE_IN | LATITUDE | LONGITUDE
232
- ALL = 0x7F80 # without LONG_UNROLL, LINE_OFF, REVERSE2 and _DEBUG_*
233
-
234
- _DIRECT3 = AZIMUTH | LATITUDE | LONGITUDE | _CAP_3 # for goedesicw only
235
- _INVERSE3 = AZIMUTH | DISTANCE | _CAP_1 # for goedesicw only
236
- _STD = STANDARD | _CAP_3 | _CAP_1 # for goedesicw only
237
- _STD_LINE = _STD | _CAP_2 | _CAP_1p # for goedesici/-w
238
-
239
- LINE_CAPS = _STD_LINE | REDUCEDLENGTH | GEODESICSCALE # .geodesici only
240
-
241
- LINE_OFF = 1 << 15 # Line without updates from parent geodesic or rhumb
242
- LONG_UNROLL = 1 << 16 # unroll C{lon2} in .Direct and .Position
243
- REVERSE2 = 1 << 17 # reverse C{azi2}
215
+ EMPTY = 0 # formerly aka NONE
216
+ _CAP_1 = 1 << 0 # for goedesici/-w
217
+ _CAP_1p = 1 << 1 # for goedesici/-w
218
+ _CAP_2 = 1 << 2
219
+ _CAP_3 = 1 << 3 # for goedesici/-w
220
+ _CAP_4 = 1 << 4 # for goedesicw
221
+ _CAP_ALL = 0x1F
222
+ # _CAP_MASK = _CAP_ALL
223
+ LATITUDE = 1 << 7 # compute latitude C{lat2}
224
+ LONGITUDE = 1 << 8 | _CAP_3 # compute longitude C{lon2}
225
+ AZIMUTH = 1 << 9 # azimuths C{azi1} and C{azi2}
226
+ DISTANCE = 1 << 10 | _CAP_1 # compute distance C{s12}
227
+ DISTANCE_IN = 1 << 11 | _CAP_1 | _CAP_1p # allow distance C{s12} in Direct
228
+ REDUCEDLENGTH = 1 << 12 | _CAP_1 | _CAP_2 # compute reduced length C{m12}
229
+ GEODESICSCALE = 1 << 13 | _CAP_1 | _CAP_2 # compute geodesic scales C{M12} and C{M21}
230
+ AREA = 1 << 14 | _CAP_4 # compute area C{S12}
231
+
232
+ STANDARD = AZIMUTH | DISTANCE | LATITUDE | LONGITUDE
233
+
234
+ _DIRECT3 = AZIMUTH | LATITUDE | LONGITUDE # for goedesicw only
235
+ _INVERSE3 = AZIMUTH | DISTANCE # for goedesicw only
236
+ _STD = STANDARD # for goedesicw only
237
+ _STD_LINE = _STD | DISTANCE_IN # for goedesici/-w
238
+
239
+ LINE_CAPS = _STD_LINE | REDUCEDLENGTH | GEODESICSCALE # .geodesici only
240
+ LONG_UNROLL = 1 << 15 # unroll C{lon2} in .Direct and .Position
241
+ # = 1 << 16 # unused
242
+ LINE_OFF = 1 << 17 # Line without updates from parent geodesic or rhumb
243
+ REVERSE2 = 1 << 18 # reverse C{azi2}
244
+ ALL = 0x7F80 | _CAP_ALL # without LONG_UNROLL, LINE_OFF, REVERSE2 and _DEBUG_*
244
245
 
245
246
  LATITUDE_LONGITUDE = LATITUDE | LONGITUDE
246
247
  LATITUDE_LONGITUDE_AREA = LATITUDE | LONGITUDE | AREA
@@ -248,35 +249,38 @@ class Caps(object): # PYCHOK
248
249
  AZIMUTH_DISTANCE = AZIMUTH | DISTANCE
249
250
  AZIMUTH_DISTANCE_AREA = AZIMUTH | DISTANCE | AREA
250
251
 
251
- _ANGLE_ONLY = 1 << 18 # angular distance C{a12} only
252
- _S_CALPs_ = 1 << 19 # (INTERNAL) GeodesicExact._GenInverse
252
+ _SALP_CALPs_ = 1 << 19 # (INTERNAL) GeodesicExact._GenInverse
253
253
 
254
254
  _DEBUG_AREA = 1 << 20 # (INTERNAL) include Line details
255
255
  _DEBUG_DIRECT = 1 << 21 # (INTERNAL) include Direct details
256
256
  _DEBUG_INVERSE = 1 << 22 # (INTERNAL) include Inverse details
257
257
  _DEBUG_LINE = 1 << 23 # (INTERNAL) include Line details
258
258
  _DEBUG_ALL = _DEBUG_AREA | _DEBUG_DIRECT | _DEBUG_INVERSE | \
259
- _DEBUG_LINE | _ANGLE_ONLY | _S_CALPs_
259
+ _DEBUG_LINE | _SALP_CALPs_
260
260
 
261
- _OUT_ALL = ALL
261
+ _OUT_ALL = ALL # see geographiclib.geodesiccapabilities.py
262
262
  _OUT_MASK = ALL | LONG_UNROLL | REVERSE2 | _DEBUG_ALL
263
263
 
264
264
  _AZIMUTH_LATITUDE_LONGITUDE = AZIMUTH | LATITUDE | LONGITUDE
265
+ _AZIMUTH_LATITUDE_LONG_UNROLL = AZIMUTH | LATITUDE | LONG_UNROLL
265
266
  _DEBUG_DIRECT_LINE = _DEBUG_DIRECT | _DEBUG_LINE
266
267
  # _DISTANCE_IN_OUT = DISTANCE_IN & _OUT_MASK # == DISTANCE_IN in .gx, .gxline
267
- _LINE = AZIMUTH | LATITUDE | LONG_UNROLL
268
268
  _REDUCEDLENGTH_GEODESICSCALE = REDUCEDLENGTH | GEODESICSCALE
269
269
  # _REDUCEDLENGTH_GEODESICSCALE_DISTANCE = REDUCEDLENGTH | GEODESICSCALE | DISTANCE
270
270
 
271
- def toStr(self, Csk, sep=_BAR_):
272
- '''Return a C{Caps} or C{outmask} as C{str} or tuple of C{str}s.
271
+ def items(self):
272
+ '''Yield all I{public} C{Caps} as 2-tuple C{(NAME, mask)}.
273
+ '''
274
+ for n, C in Caps.__class__.__dict__.items():
275
+ if isint(C) and not n.startswith(_UNDER_) \
276
+ and n.replace(_UNDER_, NN).isupper():
277
+ yield n, C
278
+
279
+ def toStr(self, Cask, sep=_BAR_):
280
+ '''Return C{Caps} or an C{outmask} as C{str} or tuple of C{str}s.
273
281
  '''
274
- s = []
275
- for c, C in itemsorted(self.__class__.__dict__):
276
- if isint(C) and (Csk & C) and int1s(C) == 1 \
277
- and (C in (Caps.REVERSE2, Caps._S_CALPs_)
278
- or c.replace(_UNDER_, NN).isupper()):
279
- s.append(c)
282
+ s = (n for n, C in sorted(Caps.items())
283
+ if C and (Cask & C) == C) # and int1s(C) <= 3
280
284
  return sep.join(s) if sep else tuple(s)
281
285
 
282
286
  Caps = Caps() # PYCHOK singleton
@@ -287,7 +291,7 @@ C{AREA} - compute area C{S12},
287
291
 
288
292
  C{AZIMUTH} - include azimuths C{azi1} and C{azi2},
289
293
 
290
- C{DISTANCE} - compute distance C{s12},
294
+ C{DISTANCE} - compute distance C{s12} and angular distance C{a12},
291
295
 
292
296
  C{DISTANCE_IN} - allow distance C{s12} in C{.Direct},
293
297
 
@@ -311,7 +315,8 @@ and C{ALL} - all of the above.
311
315
 
312
316
  C{STANDARD} = C{AZIMUTH | DISTANCE | DISTANCE_IN | LATITUDE | LONGITUDE}'''
313
317
 
314
- _KEY2Caps = dict(azi2=Caps.AZIMUTH, # see GDict._unCaps
318
+ _key2Caps = dict(a12 =Caps.DISTANCE, # in GDict._unCaps
319
+ azi2=Caps.AZIMUTH,
315
320
  lat2=Caps.LATITUDE,
316
321
  lon2=Caps.LONGITUDE,
317
322
  m12 =Caps.REDUCEDLENGTH,
@@ -322,7 +327,7 @@ _KEY2Caps = dict(azi2=Caps.AZIMUTH, # see GDict._unCaps
322
327
 
323
328
 
324
329
  class _CapsBase(_NamedBase): # in .auxilats, .geodesicx.gxbases
325
- '''(INTERNAL) Base class for C{[_]Geodesic*Exact}.
330
+ '''(INTERNAL) Base class for C{Geodesic*}, C{Geodesic*Exact}, C{Intersectool} and C{Rhumb*Base}.
326
331
  '''
327
332
  ALL = Caps.ALL
328
333
  AREA = Caps.AREA
@@ -473,8 +478,8 @@ class GDict(ADict): # XXX _NamedDict
473
478
  t = tuple(_g(self, n, dflt) for n in nTuple._Names_)
474
479
  return nTuple(t, iteration=self._iteration)
475
480
 
476
- def _2X(self, gl, _2X=_X_): # .Intesectool, .Intersector
477
- '''(INTERNAL) Rename attr tail from C{-2} to C{-X} or C{-M}.
481
+ def _2X(self, gl, _2X=_X_): # .Intersectool, .Intersector
482
+ '''(INTERNAL) Rename C{-2} attr to C{-X} or C{-M}.
478
483
  '''
479
484
  X = GDict(self)
480
485
  for n in (_lat2_, _lon2_, _azi2_, _s12_, _a12_):
@@ -488,8 +493,8 @@ class GDict(ADict): # XXX _NamedDict
488
493
  def _unCaps(self, outmask): # in .geodsolve
489
494
  '''(INTERNAL) Remove superfluous items.
490
495
  '''
491
- for k, m in _KEY2Caps.items():
492
- if k in self and not (outmask & m):
496
+ for k, C in _key2Caps.items():
497
+ if k in self and (outmask & C) != C:
493
498
  self.pop(k) # delattr(self, k)
494
499
  return self
495
500
 
@@ -531,13 +536,13 @@ class _kWrapped(object): # in .geodesicw
531
536
  <https://PyPI.org/project/geographiclib>} classes.
532
537
  '''
533
538
 
534
- @Property_RO
539
+ @property_ROnce
535
540
  def geographiclib(self):
536
541
  '''Lazily import C{geographiclib}, provided the U{geographiclib
537
542
  <https://PyPI.org/project/geographiclib>} package is installed,
538
543
  otherwise raise a C{LazyImportError}.
539
544
  '''
540
- g = _xgeographiclib(self.__class__, 1, 49)
545
+ g = _xgeographiclib(self.__class__.__module__, 1, 49)
541
546
  from geographiclib.geodesic import Geodesic
542
547
  g.Geodesic = Geodesic
543
548
  from geographiclib.geodesicline import GeodesicLine
@@ -546,7 +551,7 @@ class _kWrapped(object): # in .geodesicw
546
551
  g.Math = Math
547
552
  return g
548
553
 
549
- @Property_RO
554
+ @property_ROnce
550
555
  def Math(self):
551
556
  '''Wrap the C{geomath.Math} class, provided the U{geographiclib
552
557
  <https://PyPI.org/project/geographiclib>} package is installed,
@@ -599,7 +604,7 @@ class Rhumb8Tuple(_GTuple):
599
604
 
600
605
  @kwarg dflt: Default value for missing items (C{any}).
601
606
  @kwarg a12_m12_M12_M21_salp1_calp1_salp2_calp2: Optional keyword
602
- arguments to specify or override L{Inverse10Tuple} items.
607
+ arguments to specify or override L{Inverse10Tuple} items.
603
608
 
604
609
  @return: L{Inverse10Tuple}C{(a12, s12, salp1, calp1, salp2, calp2,
605
610
  m12, M12, M21, S12)}.
@@ -724,14 +729,15 @@ def _isfinite(x): # mimick geomath.Math.isfinite
724
729
  return _math_isfinite(x) # and fabs(x) <= _MAX
725
730
 
726
731
 
727
- def _llz2line(line, **llz2):
728
- '''(INTERNAL) Set C{line.lat2, .lon2, .azi2} from C{llz2}.
732
+ def _llz2gl(gl, **llz2): # see .geodesici._llz2G
733
+ '''(INTERNAL) Set C{gl.lat2, .lon2, .azi2} from C{llz2}.
729
734
  '''
730
735
  if llz2:
731
- llz2 = _xkwds_not(None, **_xkwds_kwds(llz2, lat2=None, lon2=None, azi2=None))
732
- if llz2:
733
- line.__dict__.update(llz2)
734
- return line
736
+ for n in (_lat2_, _lon2_, _azi2_): # _lat1_, _lon1_, _azi1_
737
+ v = llz2.get(n, None)
738
+ if v is not None:
739
+ setattr(gl, n, v)
740
+ return gl
735
741
 
736
742
 
737
743
  def _norm2(x, y): # mimick geomath.Math.norm
pygeodesy/ktm.py CHANGED
@@ -67,7 +67,7 @@ from cmath import polar
67
67
  from math import atan2, asinh, cos, cosh, degrees, fabs, sin, sinh, sqrt, tanh
68
68
 
69
69
  __all__ = _ALL_LAZY.ktm
70
- __version__ = '24.06.11'
70
+ __version__ = '24.07.16'
71
71
 
72
72
 
73
73
  class KTMError(_ValueError):
@@ -433,10 +433,7 @@ def _cma(a, b0, b1, Cn):
433
433
  @see: CPython function U{_Py_c_prod<https://GitHub.com/python/
434
434
  cpython/blob/main/Objects/complexobject.c>}.
435
435
 
436
- @note: Python function C{cmath.fsum} is no longer available,
437
- but stil mentioned in Note 4 of the comments before
438
- CPython function U{math_fsum<https://GitHub.com/python/
439
- cpython/blob/main/Modules/mathmodule.c>}
436
+ @note: Python function C{cmath.fsum} is no longer available.
440
437
  '''
441
438
  r = fsum1f_(a.real * b0.real, -a.imag * b0.imag, -b1.real, Cn)
442
439
  j = fsum1f_(a.real * b0.imag, a.imag * b0.real, -b1.imag)
pygeodesy/latlonBase.py CHANGED
@@ -41,7 +41,7 @@ from pygeodesy.namedTuples import Bounds2Tuple, LatLon2Tuple, PhiLam2Tuple, \
41
41
  Trilaterate5Tuple
42
42
  # from pygeodesy.nvectorBase import _N_vector_ # _MODS
43
43
  from pygeodesy.props import deprecated_method, Property, Property_RO, \
44
- property_RO, _update_all
44
+ property_RO, property_ROnce, _update_all
45
45
  # from pygeodesy.streprs import Fmt, hstr # from .named, _MODS
46
46
  from pygeodesy.units import _isDegrees, _isRadius, Distance_, Lat, Lon, \
47
47
  Height, Radius, Radius_, Scalar, Scalar_
@@ -54,7 +54,7 @@ from contextlib import contextmanager
54
54
  from math import asin, cos, degrees, fabs, radians
55
55
 
56
56
  __all__ = _ALL_LAZY.latlonBase
57
- __version__ = '24.06.11'
57
+ __version__ = '24.07.12'
58
58
 
59
59
  _formy = _MODS.into(formy=__name__)
60
60
 
@@ -475,12 +475,11 @@ class LatLonBase(_NamedBase):
475
475
  r = func_(phi2, self.phi, lam21, datum=D)
476
476
  return r * (D.ellipsoid.a if radius is None else radius)
477
477
 
478
- @property_RO
478
+ @property_ROnce
479
479
  def Ecef(self):
480
480
  '''Get the ECEF I{class} (L{EcefKarney}), I{once}.
481
481
  '''
482
- LatLonBase.Ecef = E = _MODS.ecef.EcefKarney # overwrite property_RO
483
- return E
482
+ return _MODS.ecef.EcefKarney
484
483
 
485
484
  @Property_RO
486
485
  def _Ecef_forward(self):
pygeodesy/lazily.py CHANGED
@@ -284,8 +284,8 @@ _ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY',
284
284
  fsums=_i('Fsum', 'DivMod2Tuple', 'Fsum2Tuple', 'ResidualError',
285
285
  'fsum', 'fsum_', 'fsumf_', 'fsum1', 'fsum1_', 'fsum1f_'),
286
286
  gars=_i('Garef', 'GARSError'),
287
- geodesici=_i('Intersectool', 'Intersectool5Tuple', 'Middle5Tuple',
288
- 'Intersector', 'Intersector5Tuple', 'XDict'),
287
+ geodesici=_i('Intersectool', 'Intersectool5Tuple', 'Intersect7Tuple',
288
+ 'Intersector', 'Intersector5Tuple', 'Middle5Tuple', 'XDict'),
289
289
  geodesicw=_i('Geodesic', 'GeodesicLine', 'Geodesic_WGS84'),
290
290
  geodesicx=_i('gx', 'gxarea', 'gxbases', 'gxline', # modules
291
291
  'GeodesicAreaExact', 'GeodesicExact', 'GeodesicLineExact', 'PolygonArea'),
@@ -343,7 +343,8 @@ _ALL_LAZY = _NamedEnum_RO(_name='_ALL_LAZY',
343
343
  'areaOf', 'boundsOf', 'centroidOf', 'fractional',
344
344
  'isclockwise', 'isconvex', 'isconvex_', 'isenclosedBy', 'ispolar',
345
345
  'luneOf', 'nearestOn5', 'perimeterOf', 'quadOf'),
346
- props=_i('Property', 'Property_RO', 'property_RO', 'property_doc_',
346
+ props=_i('Property', 'Property_RO', 'property_doc_',
347
+ 'property_RO', 'property_ROnce', 'property_ROver',
347
348
  'deprecated_class', 'deprecated_function', 'deprecated_method',
348
349
  'deprecated_Property_RO', 'deprecated_property_RO', 'DeprecationWarnings'),
349
350
  resections=_i('Collins5Tuple', 'ResectionError', 'Survey3Tuple', 'Tienstra7Tuple',
@@ -521,7 +522,7 @@ class _ALL_MODS(_internals._MODS_Base):
521
522
  _internals._MODS = _ALL_MODS = _ALL_MODS() # PYCHOK singleton
522
523
 
523
524
  __all__ = _ALL_LAZY.lazily
524
- __version__ = '24.07.09'
525
+ __version__ = '24.07.18'
525
526
 
526
527
 
527
528
  def _ALL_OTHER(*objs):
pygeodesy/ltp.py CHANGED
@@ -33,8 +33,8 @@ from pygeodesy.ltpTuples import Attitude4Tuple, ChLVEN2Tuple, ChLV9Tuple, \
33
33
  ChLVyx2Tuple, _XyzLocals4, _XyzLocals5, Xyz4Tuple
34
34
  from pygeodesy.named import _name__, _name2__, _NamedBase, notOverloaded
35
35
  from pygeodesy.namedTuples import LatLon3Tuple, LatLon4Tuple, Vector3Tuple
36
- from pygeodesy.props import Property, Property_RO, property_doc_, property_RO, \
37
- _update_all
36
+ from pygeodesy.props import Property, Property_RO, property_doc_, \
37
+ property_ROver, _update_all
38
38
  from pygeodesy.streprs import Fmt, strs, unstr
39
39
  from pygeodesy.units import Bearing, Degrees, _isHeight, Meter
40
40
  from pygeodesy.utily import cotd, _loneg, sincos2d, sincos2d_, tand, tand_, \
@@ -44,7 +44,7 @@ from pygeodesy.vector3d import _ALL_LAZY, Vector3d
44
44
  # from math import fabs, floor as _floor # from .fmath, .fsums
45
45
 
46
46
  __all__ = _ALL_LAZY.ltp
47
- __version__ = '24.06.11'
47
+ __version__ = '24.07.12'
48
48
 
49
49
  _height0_ = _height_ + _0_
50
50
  _narrow_ = 'narrow'
@@ -722,15 +722,15 @@ class _ChLV(object):
722
722
  t = Y_X_h_lat_lon_h + (self, self._t0, None) # PYCHOK _t0
723
723
  return ChLV9Tuple(t, name=name)
724
724
 
725
- @property_RO
725
+ @property_ROver
726
726
  def _enh_n_h(self):
727
727
  '''(INTERNAL) Get C{ChLV*.reverse} args[1:4] names, I{once}.
728
728
  '''
729
- _ChLV._enh_n_h = t = _args_kwds_names(_ChLV.reverse)[1:4] # overwrite property_RO
729
+ t = _args_kwds_names(_ChLV.reverse)[1:4]
730
730
  # assert _args_kwds_names( ChLV.reverse)[1:4] == t
731
731
  # assert _args_kwds_names(ChLVa.reverse)[1:4] == t
732
732
  # assert _args_kwds_names(ChLVe.reverse)[1:4] == t
733
- return t
733
+ return t # overwrite propertyROver
734
734
 
735
735
  def forward(self, latlonh, lon=None, height=0, M=None, **name): # PYCHOK no cover
736
736
  '''Convert WGS84 geodetic to I{Swiss} projection coordinates. I{Must be overloaded}.
pygeodesy/named.py CHANGED
@@ -34,7 +34,7 @@ from pygeodesy.streprs import attrs, Fmt, lrstrip, pairs, reprs, unstr
34
34
  # from pygeodesy.units import _toUnit # _MODS
35
35
 
36
36
  __all__ = _ALL_LAZY.named
37
- __version__ = '24.06.24'
37
+ __version__ = '24.07.12'
38
38
 
39
39
  _COMMANL_ = _COMMA_ + _NL_
40
40
  _COMMASPACEDOT_ = _COMMASPACE_ + _DOT_
@@ -1231,9 +1231,9 @@ def modulename(clas, prefixed=None): # in .basics._xversion
1231
1231
  @return: The B{C{class}}'s C{[module.]class} name (C{str}).
1232
1232
  '''
1233
1233
  try:
1234
- n = clas.__name__
1234
+ n = clas.__name__
1235
1235
  except AttributeError:
1236
- n = _dunder_name_
1236
+ n = clas if isstr(clas) else _dunder_name_
1237
1237
  if prefixed or (classnaming() if prefixed is None else False):
1238
1238
  try:
1239
1239
  m = clas.__module__.rsplit(_DOT_, 1)
pygeodesy/nvectorBase.py CHANGED
@@ -29,7 +29,7 @@ from pygeodesy.named import _xother3, _under
29
29
  from pygeodesy.namedTuples import Trilaterate5Tuple, Vector3Tuple, \
30
30
  Vector4Tuple, map1
31
31
  from pygeodesy.props import deprecated_method, Property_RO, property_doc_, \
32
- property_RO, _update_all
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
35
  from pygeodesy.utily import sincos2d, _unrollon, _unrollon3
@@ -38,7 +38,7 @@ from pygeodesy.vector3d import Vector3d, _xyzhdlln4
38
38
  from math import fabs, sqrt
39
39
 
40
40
  __all__ = _ALL_LAZY.nvectorBase
41
- __version__ = '24.06.12'
41
+ __version__ = '24.07.12'
42
42
 
43
43
 
44
44
  class NvectorBase(Vector3d): # XXX kept private
@@ -78,12 +78,11 @@ class NvectorBase(Vector3d): # XXX kept private
78
78
  '''
79
79
  return self._datum
80
80
 
81
- @property_RO
81
+ @property_ROnce
82
82
  def Ecef(self):
83
83
  '''Get the ECEF I{class} (L{EcefKarney}), I{once}.
84
84
  '''
85
- NvectorBase.Ecef = E = _MODS.ecef.EcefKarney # overwrite property_RO
86
- return E
85
+ return _MODS.ecef.EcefKarney
87
86
 
88
87
  @property_RO
89
88
  def ellipsoidalNvector(self):
pygeodesy/props.py CHANGED
@@ -25,7 +25,7 @@ from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, \
25
25
  from functools import wraps as _wraps
26
26
 
27
27
  __all__ = _ALL_LAZY.props
28
- __version__ = '24.05.26'
28
+ __version__ = '24.07.23'
29
29
 
30
30
  _class_ = 'class'
31
31
  _dont_use_ = _DEPRECATED_ + ", don't use."
@@ -48,11 +48,12 @@ def _allPropertiesOf(Clas_or_inst, *Bases, **excls):
48
48
  except AttributeError:
49
49
  raise
50
50
  S = () # not an inst
51
- B = Bases or _PropertyBase
52
- _isa = isinstance
51
+ B = Bases or _PropertyBase
52
+ P = _property_RO___
53
53
  for C in S:
54
54
  for n, p in C.__dict__.items():
55
- if _isa(p, B) and p.name == n and n not in excls:
55
+ if isinstance(p, B) and p.name == n and not (
56
+ isinstance(p, P) or n in excls):
56
57
  yield p
57
58
 
58
59
 
@@ -251,9 +252,9 @@ class Property_RO(_PropertyBase):
251
252
  <https://Docs.Python.org/3/library/functools.html#functools.cache>}
252
253
  to I{cache} or I{memoize} the property value.
253
254
 
254
- @see: Luciano Ramalho, "Fluent Python", O'Reilly, Example 19-24, 2016
255
- p. 636 or Example 22-28, 2022 p. 870 and U{class Property
256
- <https://docs.Python.org/3/howto/descriptor.html>}.
255
+ @see: Luciano Ramalho, "Fluent Python", O'Reilly, 2016 p. 636
256
+ Example 19-24 or 2022 p. 870 Example 22-28 and U{class
257
+ Property<https://docs.Python.org/3/howto/descriptor.html>}.
257
258
  '''
258
259
  _fget = method if _FOR_DOCS else self._fget # XXX force method.__doc__ to epydoc
259
260
  _PropertyBase.__init__(self, method, _fget, self._fset_error)
@@ -355,6 +356,61 @@ class property_RO(_PropertyBase):
355
356
  inst.__dict__.pop(uname)
356
357
 
357
358
 
359
+ class _property_RO___(_PropertyBase):
360
+ # No __doc__ on purpose
361
+
362
+ def __init__(self, method, doc=NN): # PYCHOK expected
363
+ '''New C{property_ROnce} or C{property_ROver}, holding a singleton value as
364
+ class attribute for all instances of that class and overwriting C{self},
365
+ the C{property_ROver} instance in the 1st invokation.
366
+
367
+ @see: L{property_RO} for further details.
368
+ '''
369
+ _PropertyBase.__init__(self, method, self._fget, self._fset_error, doc=doc)
370
+
371
+ def _fdel(self, unused): # PYCHOK no cover
372
+ '''Silently ignored, always.
373
+ '''
374
+ pass
375
+
376
+ def _update(self, *unused): # PYCHOK signature
377
+ '''(INTERNAL) No-op, ignore updates.
378
+ '''
379
+ pass
380
+
381
+
382
+ class property_ROnce(_property_RO___):
383
+ # No __doc__ on purpose
384
+
385
+ def _fget(self, inst):
386
+ '''Get the C{property} value, only I{once} and memoize/cache it.
387
+ '''
388
+ try:
389
+ v = self._val
390
+ except AttributeError:
391
+ v = self._val = self.method(inst)
392
+ return v
393
+
394
+
395
+ class property_ROver(_property_RO___):
396
+ # No __doc__ on purpose
397
+
398
+ def _fget(self, inst):
399
+ '''Get the C{property} value I{once} and overwrite C{self}, this C{property} instance.
400
+ '''
401
+ v = self.method(inst)
402
+ n = self.name
403
+ C = inst.__class__
404
+ for c in C.__mro__: # [:-1]
405
+ if getattr(c, n, None) is self:
406
+ setattr(c, n, v) # overwrite property_ROver
407
+ break
408
+ else:
409
+ n = _DOT_(C.__name__, n)
410
+ raise _AssertionError(_EQUALSPACED_(n, v))
411
+ return v
412
+
413
+
358
414
  class _NamedProperty(property):
359
415
  '''Class C{property} with retrievable name.
360
416
  '''
@@ -372,16 +428,18 @@ def property_doc_(doc):
372
428
 
373
429
  @example:
374
430
 
375
- >>> @property_doc_("documentation text.")
376
- >>> def name(self):
377
- >>> ...
431
+ >>>class Clas(object):
432
+ >>>
433
+ >>> @property_doc_("documentation text.")
434
+ >>> def name(self):
435
+ >>> ...
378
436
  >>>
379
- >>> @name.setter
380
- >>> def name(self, value):
381
- >>> ...
437
+ >>> @name.setter
438
+ >>> def name(self, value):
439
+ >>> ...
382
440
  '''
383
- # See Luciano Ramalho, "Fluent Python", O'Reilly, Example 7-23,
384
- # 2016 p. 212+, 2022 p. 331+, Example 9-22 and <https://
441
+ # See Luciano Ramalho, "Fluent Python", O'Reilly, 2016 p. 212+
442
+ # Example 7-23 or 2022 p. 331+ Example 9-22 and <https://
385
443
  # Python-3-Patterns-Idioms-Test.ReadTheDocs.io/en/latest/PythonDecorators.html>
386
444
 
387
445
  def _documented_property(method):
@@ -396,8 +454,8 @@ def property_doc_(doc):
396
454
  def _deprecated(call, kind, qual_d):
397
455
  '''(INTERNAL) Decorator for DEPRECATED functions, methods, etc.
398
456
 
399
- @see: Brett Slatkin, "Effective Python", page 105, 2nd ed,
400
- Addison-Wesley, 2019.
457
+ @see: Brett Slatkin, "Effective Python", 2019 page 105, 2nd
458
+ ed, Addison-Wesley.
401
459
  '''
402
460
  doc = _docof(call)
403
461
 
pygeodesy/rhumb/solve.py CHANGED
@@ -21,7 +21,7 @@ from pygeodesy.solveBase import _SolveGDictBase, _SolveGDictLineBase
21
21
  from pygeodesy.utily import _unrollon, _Wrap, wrap360
22
22
 
23
23
  __all__ = _ALL_LAZY.rhumb_solve
24
- __version__ = '24.06.28'
24
+ __version__ = '24.07.11'
25
25
 
26
26
 
27
27
  class _RhumbSolveBase(_SolveGDictBase):
@@ -173,7 +173,7 @@ class RhumbSolve(_RhumbSolveBase):
173
173
  return r
174
174
 
175
175
  def _GDictInverse(self, lat1, lon1, lat2, lon2, *unused, **floats): # PYCHOK signature
176
- '''(INTERNAL) Get C{_GenInverse}-like result as an 8-item C{GDict}, but I{without} C{_S_CALPs_}.
176
+ '''(INTERNAL) Get C{_GenInverse}-like result as an 8-item C{GDict}, but I{without} C{_SALP_CALPs_}.
177
177
  '''
178
178
  i = _RhumbSolveBase._GDictInverse(self, lat1, lon1, lat2, lon2, **floats)
179
179
  a = _over(float(i.s12), self._mpd) # for .Inverse1