pygeodesy 25.5.25__py2.py3-none-any.whl → 25.7.25__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.
@@ -17,7 +17,7 @@ U{GeographicLib<https://GeographicLib.SourceForge.io>} documentation.
17
17
  # make sure int/int division yields float quotient
18
18
  from __future__ import division as _; del _ # noqa: E702 ;
19
19
 
20
- from pygeodesy.basics import isodd, unsigned0
20
+ from pygeodesy.basics import _copysign, isodd, unsigned0
21
21
  from pygeodesy.constants import NAN, _0_0, _0_5, _720_0
22
22
  from pygeodesy.internals import printf, typename
23
23
  # from pygeodesy.interns import _COMMASPACE_ # from .lazily
@@ -31,19 +31,16 @@ from pygeodesy.props import Property, Property_RO, property_RO
31
31
  from math import fmod as _fmod
32
32
 
33
33
  __all__ = ()
34
- __version__ = '25.05.12'
34
+ __version__ = '25.06.04'
35
35
 
36
36
 
37
37
  class GeodesicAreaExact(_NamedBase):
38
- '''Area and perimeter of a geodesic polygon, an enhanced
39
- version of I{Karney}'s Python class U{PolygonArea
40
- <https://GeographicLib.SourceForge.io/html/python/
41
- code.html#module-geographiclib.polygonarea>} using
42
- the more accurate surface area.
43
-
44
- @note: The name of this class C{*Exact} is a misnomer, see
45
- I{Karney}'s comments at C++ attribute U{GeodesicExact._c2
46
- <https://GeographicLib.SourceForge.io/C++/doc/
38
+ '''Area and perimeter of a geodesic polygon, an enhanced version of I{Karney}'s
39
+ Python class U{PolygonArea<https://GeographicLib.SourceForge.io/html/python/
40
+ code.html#module-geographiclib.polygonarea>} using the more accurate surface area.
41
+
42
+ @note: The name of this class C{*Exact} is a misnomer, see I{Karney}'s comments at
43
+ C++ attribute U{GeodesicExact._c2<https://GeographicLib.SourceForge.io/C++/doc/
47
44
  GeodesicExact_8cpp_source.html>}.
48
45
  '''
49
46
  _Area = None
@@ -140,46 +137,45 @@ class GeodesicAreaExact(_NamedBase):
140
137
 
141
138
  @Property_RO
142
139
  def area0x(self):
143
- '''Get the ellipsoid's surface area (C{meter} I{squared}),
144
- more accurate for very I{oblate} ellipsoids.
140
+ '''Get the ellipsoid's surface area (C{meter} I{squared}), more accurate
141
+ for very I{oblate} ellipsoids.
145
142
  '''
146
143
  return self.ellipsoid.areax # not .area!
147
144
 
148
145
  area0 = area0x # for C{geographiclib} compatibility
149
146
 
150
- def Compute(self, reverse=False, sign=True):
147
+ def Compute(self, reverse=False, sign=True, polar=False):
151
148
  '''Compute the accumulated perimeter and area.
152
149
 
153
- @kwarg reverse: If C{True}, clockwise traversal counts as a
154
- positive area instead of counter-clockwise
155
- (C{bool}).
156
- @kwarg sign: If C{True}, return a signed result for the area if
157
- the polygon is traversed in the "wrong" direction
158
- instead of returning the area for the rest of the
159
- earth.
160
-
161
- @return: L{Area3Tuple}C{(number, perimeter, area)} with the
162
- number of points, the perimeter in C{meter} and the
163
- area in C{meter**2}. The perimeter includes the
164
- length of a final edge, connecting the current to
165
- the initial point, if this polygon was initialized
166
- with C{polyline=False}. For perimeter only, i.e.
167
- C{polyline=True}, area is C{NAN}.
168
-
169
- @note: Arbitrarily complex polygons are allowed. In the case
170
- of self-intersecting polygons, the area is accumulated
171
- "algebraically". E.g., the areas of the 2 loops in a
172
- I{figure-8} polygon will partially cancel.
150
+ @kwarg reverse: If C{True}, clockwise traversal counts as a positive area instead
151
+ of counter-clockwise (C{bool}).
152
+ @kwarg sign: If C{True}, return a signed result for the area if the polygon is
153
+ traversed in the "wrong" direction instead of returning the area for
154
+ the rest of the earth.
155
+ @kwarg polar: Use C{B{polar}=True} if the polygon encloses a pole (C{bool}), see
156
+ function L{ispolar<pygeodesy.points.ispolar>} and U{area of a polygon
157
+ enclosing a pole<https://GeographicLib.SourceForge.io/C++/doc/
158
+ classGeographicLib_1_1GeodesicExact.html#a3d7a9155e838a09a48dc14d0c3fac525>}.
159
+
160
+ @return: L{Area3Tuple}C{(number, perimeter, area)} with the number of points, the
161
+ perimeter in C{meter} and the (signed) area in C{meter**2}. The perimeter
162
+ includes the length of a final edge, connecting the current to the initial
163
+ point, if this polygon was initialized with C{polyline=False}. For perimeter
164
+ only, i.e. C{polyline=True}, area is C{NAN}.
165
+
166
+ @note: Arbitrarily complex polygons are allowed. In the case of self-intersecting
167
+ polygons, the area is accumulated "algebraically". E.g., the areas of both
168
+ loops in a I{figure-8} polygon will partially cancel.
173
169
 
174
170
  @note: More points and edges can be added after this call.
175
171
  '''
176
172
  r, n = None, self.num
177
173
  if n < 2:
178
174
  p = _0_0
179
- a = NAN if self.polyline else p
175
+ a = NAN if n > 0 and self.polyline else p
180
176
  elif self._Area:
181
177
  r = self._Inverse(self.lat1, self.lon1, self.lat0, self.lon0)
182
- a = self._reduced(r.S12, reverse, sign, r.xing)
178
+ a = self._reduced(r.S12, r.xing, n, reverse=reverse, sign=sign, polar=polar)
183
179
  p = self._Peri.Sum(r.s12)
184
180
  else:
185
181
  p = self._Peri.Sum()
@@ -279,7 +275,7 @@ class GeodesicAreaExact(_NamedBase):
279
275
  t = _COMMASPACE_.join(pairs(d, prec=10))
280
276
  printf('%s %s: %s (%s)', self.named2, n, t, callername(up=2))
281
277
 
282
- def _reduced(self, S12, reverse, sign, xing):
278
+ def _reduced(self, S12, xing, n, reverse=False, sign=True, polar=False):
283
279
  '''(INTERNAL) Accumulate and reduce area to allowed range.
284
280
  '''
285
281
  a0 = self.area0x
@@ -296,6 +292,8 @@ class GeodesicAreaExact(_NamedBase):
296
292
  a = A.Add(-a0)
297
293
  elif a <= -a0_:
298
294
  a = A.Add( a0)
295
+ if polar: # see .geodesicw._gwrapped.Geodesic.Area
296
+ a = A.Add(_copysign(a0 * _0_5 * n, a)) # - if reverse or sign?
299
297
  return unsigned0(a)
300
298
 
301
299
  def Reset(self):
@@ -313,63 +311,53 @@ class GeodesicAreaExact(_NamedBase):
313
311
 
314
312
  Clear = Reset
315
313
 
316
- def TestEdge(self, azi, s, reverse=False, sign=True):
314
+ def TestEdge(self, azi, s, **reverse_sign_polar):
317
315
  '''Compute the properties for a tentative, additional edge
318
316
 
319
317
  @arg azi: Azimuth at the current the point (compass C{degrees}).
320
318
  @arg s: Length of the edge (C{meter}).
321
- @kwarg reverse: If C{True}, clockwise traversal counts as a
322
- positive area instead of counter-clockwise
323
- (C{bool}).
324
- @kwarg sign: If C{True}, return a signed result for the area if
325
- the polygon is traversed in the "wrong" direction
326
- instead of returning the area for the rest of the
327
- earth.
328
-
329
- @return: L{Area3Tuple}C{(number, perimeter, area)}.
319
+ @kwarg reverse_sign_polar: Optional C{B{reverse}=False}, C{B{sign}=True} and
320
+ C{B{polar}=False} keyword arguments, see method L{Compute}.
330
321
 
331
- @raise GeodesicError: No points.
322
+ @return: L{Area3Tuple}C{(number, perimeter, area)}, with C{perimeter} and
323
+ C{area} both C{NAN} for insuffcient C{number} of points.
332
324
  '''
333
- n = self.num + 1
334
- p = self._Peri.Sum(s)
335
- if self.polyline:
336
- a, r = NAN, None
337
- elif n < 2:
338
- raise GeodesicError(num=self.num)
325
+ r, n = None, self.num + 1
326
+ if n < 2: # raise GeodesicError(num=self.num)
327
+ a = p = NAN # like .test_Planimeter19
339
328
  else:
340
- d = self._Direct(azi, s)
341
- r = self._Inverse(d.lat2, d.lon2, self.lat0, self.lon0)
342
- a = self._reduced(d.S12 + r.S12, reverse, sign, d.xing + r.xing)
343
- p += r.s12
329
+ p = self._Peri.Sum(s)
330
+ if self.polyline:
331
+ a = NAN
332
+ else:
333
+ d = self._Direct(azi, s)
334
+ r = self._Inverse(d.lat2, d.lon2, self.lat0, self.lon0)
335
+ a = self._reduced(d.S12 + r.S12, d.xing + r.xing, n, **reverse_sign_polar)
336
+ p += r.s12
344
337
  if self.verbose: # PYCHOK no cover
345
338
  self._print(n, p, a, r, azi=azi, s=s)
346
339
  return Area3Tuple(n, p, a)
347
340
 
348
- def TestPoint(self, lat, lon, reverse=False, sign=True):
341
+ def TestPoint(self, lat, lon, **reverse_sign_polar):
349
342
  '''Compute the properties for a tentative, additional vertex
350
343
 
351
344
  @arg lat: Latitude of the point (C{degrees}).
352
345
  @arg lon: Longitude of the point (C{degrees}).
353
- @kwarg reverse: If C{True}, clockwise traversal counts as a
354
- positive area instead of counter-clockwise
355
- (C{bool}).
356
- @kwarg sign: If C{True}, return a signed result for the area if
357
- the polygon is traversed in the "wrong" direction
358
- instead of returning the area for the rest of the
359
- earth.
346
+ @kwarg reverse_sign_polar: Optional C{B{reverse}=False}, C{B{sign}=True} and
347
+ C{B{polar}=False} keyword arguments, see method L{Compute}.
360
348
 
361
349
  @return: L{Area3Tuple}C{(number, perimeter, area)}.
362
350
  '''
363
351
  r, n = None, self.num + 1
364
352
  if n < 2:
365
353
  p = _0_0
366
- a = NAN if self.polyline else p
354
+ a = NAN if self.polyline else p
367
355
  else:
368
356
  i = self._Inverse(self.lat1, self.lon1, lat, lon)
369
357
  p = self._Peri.Sum(i.s12)
370
358
  if self._Area:
371
359
  r = self._Inverse(lat, lon, self.lat0, self.lon0)
372
- a = self._reduced(i.S12 + r.S12, reverse, sign, i.xing + r.xing)
360
+ a = self._reduced(i.S12 + r.S12, i.xing + r.xing, n, **reverse_sign_polar)
373
361
  p += r.s12
374
362
  else:
375
363
  a = NAN
@@ -9,7 +9,7 @@ U{GeographicLib<https://GeographicLib.SourceForge.io>} documentation.
9
9
  '''
10
10
 
11
11
  from pygeodesy.basics import isodd, _MODS
12
- from pygeodesy.constants import _EPSmin as _TINY, _0_0
12
+ from pygeodesy.constants import _EPSmin as _TINY, _0_0, isfinite
13
13
  from pygeodesy.errors import _or, _xkwds_item2
14
14
  from pygeodesy.fmath import hypot as _hypot
15
15
  # from pygeodesy.interns import _numpy_ # _MODS
@@ -20,7 +20,7 @@ from pygeodesy.karney import _CapsBase, GeodesicError, _2cos2x, \
20
20
  from math import fabs, ldexp as _ldexp
21
21
 
22
22
  __all__ = ()
23
- __version__ = '24.09.07'
23
+ __version__ = '25.06.01'
24
24
 
25
25
  # valid C{nC4}s and C{C4order}s, see _xnC4 below
26
26
  _nC4s = {24: 2900, 27: 4032, 30: 5425}
@@ -147,12 +147,23 @@ def _sinf1cos2d(lat, f1):
147
147
  return sbet, (cbet if fabs(cbet) > _TINY else _TINY)
148
148
 
149
149
 
150
+ def _toNAN(outmask, *args):
151
+ '''(INTERNAL) Is any C{arg} not finite?
152
+ '''
153
+ if (outmask & _CapsBase.NONFINITONAN): # Caps.NONFINITONAN
154
+ for arg in args:
155
+ if not isfinite(arg):
156
+ return True
157
+ return False
158
+
159
+
150
160
  def _xnC4(**name_nC4):
151
161
  '''(INTERNAL) Validate C{C4order}.
152
162
  '''
153
163
  n, nC4 = _xkwds_item2(name_nC4)
154
164
  if nC4 not in _nC4s or not isinstance(nC4, int):
155
- raise GeodesicError(n, nC4, txt_not_=_or(*map(str, _nC4s)))
165
+ t = map(str, _nC4s)
166
+ raise GeodesicError(n, nC4, txt_not_=_or(*t))
156
167
  return _nC4s[nC4]
157
168
 
158
169
 
@@ -37,13 +37,14 @@ from __future__ import division as _; del _ # noqa: E702 ;
37
37
  # - s and c prefixes mean sin and cos
38
38
 
39
39
  # from pygeodesy.basics import _xinstanceof # _MODS
40
- from pygeodesy.constants import NAN, _EPSqrt as _TOL, _0_0, _1_0, \
41
- _180_0, _2__PI, _copysign_1_0, isfinite
40
+ from pygeodesy.constants import NAN, _EPSqrt as _TOL, \
41
+ _0_0, _1_0, _180_0, _2__PI, \
42
+ _copysign_1_0, isfinite
42
43
  from pygeodesy.errors import _xError, _xkwds_pop2
43
44
  from pygeodesy.fsums import fsumf_, fsum1f_
44
45
  from pygeodesy.geodesicx.gxbases import _cosSeries, _GeodesicBase, \
45
46
  _sincos12, _sin1cos2, \
46
- _sinf1cos2d, _TINY
47
+ _sinf1cos2d, _TINY, _toNAN
47
48
  # from pygeodesy.geodesicw import _Intersecant2 # _MODS
48
49
  from pygeodesy.lazily import _ALL_DOCS, _ALL_MODS as _MODS
49
50
  from pygeodesy.karney import _around, _atan2d, Caps, GDict, _fix90, \
@@ -55,7 +56,7 @@ from pygeodesy.utily import atan2, atan2d as _atan2d_reverse, sincos2
55
56
  from math import cos, degrees, fabs, floor, radians, sin
56
57
 
57
58
  __all__ = ()
58
- __version__ = '25.05.12'
59
+ __version__ = '25.05.28'
59
60
 
60
61
  _glXs = [] # instances of C{[_]GeodesicLineExact} to be updated
61
62
 
@@ -91,6 +92,7 @@ class _GeodesicLineExact(_GeodesicBase):
91
92
  # _salp1 = _calp1 = NAN
92
93
  # _somg1 = _comg1 = NAN
93
94
  # _ssig1 = _csig1 = NAN
95
+ # _toNAN = False
94
96
 
95
97
  def __init__(self, gX, lat1, lon1, azi1, caps, **name_):
96
98
  '''(INTERNAL) New C{[_]GeodesicLineExact} instance.
@@ -104,6 +106,7 @@ class _GeodesicLineExact(_GeodesicBase):
104
106
  salp1, calp1 = _sincos2d(_around(azi1))
105
107
  if name_:
106
108
  self.name = name_
109
+ self._toNAN = _toNAN(caps, lat1, lon1, azi1, salp1, calp1)
107
110
 
108
111
  self._gX = gX # GeodesicExact only
109
112
  self._lat1 = lat1 = _fix90(lat1)
@@ -297,9 +300,9 @@ class _GeodesicLineExact(_GeodesicBase):
297
300
  gX = self.geodesic # ._gX
298
301
  r = GDict(a12=NAN, s12=NAN) # both a12 and s12, always
299
302
 
300
- if not isfinite(s12_a12):
303
+ if self._toNAN or not isfinite(s12_a12): # _toNAN(outmask, s12_a12)?
301
304
  # E2 = sig12 = ssig12 = csig12 = NAN
302
- return r._toNAN(outmask)
305
+ return r._toNAN(outmask | Cs.NONFINITONAN) # for backward compatibility
303
306
  elif arcmode: # s12_a12 is (spherical) arc length
304
307
  r.set_(a12=s12_a12)
305
308
  sig12 = radians(s12_a12)
pygeodesy/geoids.py CHANGED
@@ -355,7 +355,7 @@ class _GeoidBase(_HeightBase):
355
355
  # build grid axis, hi = lo + (n - 1) * d
356
356
  m, a = len2(frange(lo, n, d))
357
357
  if m != n:
358
- raise LenError(self.__class__, grid=m, **{name: n})
358
+ raise LenError(type(self), grid=m, **{name: n})
359
359
  if d < 0:
360
360
  d, a = -d, list(reversed(a))
361
361
  a = self.numpy.array(a)
pygeodesy/hausdorff.py CHANGED
@@ -85,7 +85,7 @@ from pygeodesy import unitsBase as _unitsBase # _Str_..., _xUnit, _xUnits
85
85
  from random import Random
86
86
 
87
87
  __all__ = _ALL_LAZY.hausdorff
88
- __version__ = '25.05.21'
88
+ __version__ = '25.05.26'
89
89
 
90
90
  _formy = _MODS.into(formy=__name__)
91
91
 
@@ -124,7 +124,7 @@ class Hausdorff(_Named):
124
124
  @raise HausdorffError: Insufficient number of B{C{point1s}} or an invalid
125
125
  B{C{point1}}, B{C{seed}} or B{C{units}}.
126
126
  '''
127
- name, kwds = _name2__(**name__kwds) # name__=self.__class__
127
+ name, kwds = _name2__(**name__kwds) # name__=type(self)
128
128
  if name:
129
129
  self.name = name
130
130
 
pygeodesy/heights.py CHANGED
@@ -92,7 +92,7 @@ from pygeodesy.units import _isDegrees, Float_, Int_
92
92
  # from math import radians # from .points
93
93
 
94
94
  __all__ = _ALL_LAZY.heights
95
- __version__ = '25.05.12'
95
+ __version__ = '25.05.26'
96
96
 
97
97
  _error_ = 'error'
98
98
  _formy = _MODS.into(formy=__name__)
@@ -212,7 +212,7 @@ class _HeightNamed(_Named): # in .geoids
212
212
  n, lats = len2(lats)
213
213
  m, lons = len2(lons)
214
214
  if n != m: # format a LenError, but raise self._Error
215
- e = LenError(self.__class__, lats=n, lons=m, txt=None)
215
+ e = LenError(type(self), lats=n, lons=m, txt=None)
216
216
  raise self._Error(str(e))
217
217
  llis = [LLiC(*t, datum=d) for t in zip(lats, lons)]
218
218
  return llis
@@ -363,13 +363,13 @@ class _HeightBase(_HeightNamed): # in .geoids
363
363
  def numpy(self):
364
364
  '''Get the C{numpy} module or C{None}.
365
365
  '''
366
- return _xnumpy(self.__class__, 1, 9) # overwrite property_ROver
366
+ return _xnumpy(type(self), 1, 9) # overwrite property_ROver
367
367
 
368
368
  @property_ROver
369
369
  def scipy(self):
370
370
  '''Get the C{scipy} module or C{None}.
371
371
  '''
372
- return _xscipy(self.__class__, 1, 2) # overwrite property_ROver
372
+ return _xscipy(type(self), 1, 2) # overwrite property_ROver
373
373
 
374
374
  @property_ROver
375
375
  def scipy_interpolate(self):
pygeodesy/internals.py CHANGED
@@ -132,7 +132,7 @@ class _MODS_Base(object):
132
132
  def name(self):
133
133
  '''Get this name (C{str}).
134
134
  '''
135
- return typename(self.__class__)
135
+ return typename(type(self))
136
136
 
137
137
  @_Property_RO
138
138
  def nix2(self): # PYCHOK no cover
@@ -686,7 +686,7 @@ def _versions(sep=_SPACE_):
686
686
 
687
687
 
688
688
  __all__ = tuple(map(typename, (machine, print_, printf, typename)))
689
- __version__ = '25.04.14'
689
+ __version__ = '25.05.26'
690
690
 
691
691
  if __name__ == _DMAIN_:
692
692
 
pygeodesy/interns.py CHANGED
@@ -443,15 +443,15 @@ _LR_PAIRS = {_LANGLE_: _RANGLE_,
443
443
 
444
444
  __all__ = (_NN_, # NOT MISSING!
445
445
  Str_.__name__) # classes
446
- __version__ = '25.04.12'
446
+ __version__ = '25.05.26'
447
447
 
448
448
  if __name__ == _DMAIN_:
449
449
 
450
- def _main():
450
+ def _main(globalocals):
451
451
  from pygeodesy import itemsorted, printf
452
452
 
453
453
  t = b = 0
454
- for n, v in itemsorted(locals(), asorted=False, reverse=True):
454
+ for n, v in itemsorted(globalocals, asorted=False, reverse=True):
455
455
  if n.endswith(_UNDER_) and n.startswith(_UNDER_) and \
456
456
  not n.startswith(_DUNDER_):
457
457
  t += 1
@@ -459,10 +459,10 @@ if __name__ == _DMAIN_:
459
459
  m = n[1:-1]
460
460
  if m != v and m.replace(_UNDER_, _SPACE_) != v:
461
461
  printf('%4d: %s = %r', t, n, v)
462
- n = len(locals())
462
+ n = len(globalocals)
463
463
  printf('%4d (%d) names, %s chars total, %.2f chars avg', t, n, b, float(b) / t, nl=1)
464
464
 
465
- _main()
465
+ _main(globals()) # or locals()
466
466
 
467
467
  # **) MIT License
468
468
  #
pygeodesy/karney.py CHANGED
@@ -166,7 +166,7 @@ from pygeodesy.utily import atan2d, sincos2d, tand, _unrollon, fabs
166
166
  # from math import fabs # from .utily
167
167
 
168
168
  __all__ = _ALL_LAZY.karney
169
- __version__ = '25.05.12'
169
+ __version__ = '25.05.28'
170
170
 
171
171
  _2_4_ = '2.4'
172
172
  _K_2_0 = _getenv(_PYGEODESY_ENV(typename(_xgeographiclib)[2:]), _2_)
@@ -240,6 +240,8 @@ class Caps(object):
240
240
 
241
241
  C{LONG_UNROLL} - unroll C{lon2} in method C{.Direct},
242
242
 
243
+ C{NONFINITONAN} - set all C{GDict} items to C{NAN} iff any argument is C{non-finite}.
244
+
243
245
  C{REDUCEDLENGTH} - compute the reduced length C{m12},
244
246
 
245
247
  C{REVERSE2} - reverse azimuth C{azi2} by 180 degrees,
@@ -274,7 +276,7 @@ class Caps(object):
274
276
 
275
277
  LINE_CAPS = STANDARD_LINE | REDUCEDLENGTH | GEODESICSCALE # .geodesici only
276
278
  LONG_UNROLL = 1 << 15 # unroll C{lon2} in .Direct and .Position
277
- # = 1 << 16 # unused
279
+ NONFINITONAN = 1 << 16 # see method GDict._toNAN
278
280
  LINE_OFF = 1 << 17 # Line without updates from parent geodesic or rhumb
279
281
  REVERSE2 = 1 << 18 # reverse C{azi2}
280
282
  ALL = 0x7F80 | _CAP_ALL # without LONG_UNROLL, LINE_OFF, REVERSE2 and _DEBUG_*
@@ -327,9 +329,12 @@ if _FOR_DOCS: # PYCHOK force ...
327
329
  else:
328
330
  Caps = Caps() # PYCHOK singleton
329
331
 
330
- _key2Caps = dict(a12 =Caps.DISTANCE, # in GDict._unCaps
332
+ _key2Caps = dict(a12 =Caps.DISTANCE, # in GDict._toNAN, -._unCaps
333
+ azi1=Caps.AZIMUTH,
331
334
  azi2=Caps.AZIMUTH,
335
+ lat1=Caps.LATITUDE,
332
336
  lat2=Caps.LATITUDE,
337
+ lon1=Caps.LONGITUDE,
333
338
  lon2=Caps.LONGITUDE,
334
339
  m12 =Caps.REDUCEDLENGTH,
335
340
  M12 =Caps.GEODESICSCALE,
@@ -353,6 +358,7 @@ class _CapsBase(_NamedBase): # in .auxilats, .geodesicx.gxbases
353
358
  LINE_OFF = Caps.LINE_OFF
354
359
  LONGITUDE = Caps.LONGITUDE
355
360
  LONG_UNROLL = Caps.LONG_UNROLL
361
+ NONFINITONAN = Caps.NONFINITONAN
356
362
  REDUCEDLENGTH = Caps.REDUCEDLENGTH
357
363
  STANDARD = Caps.STANDARD
358
364
  STANDARD_LINE = Caps.STANDARD_LINE # for geodesici
@@ -449,11 +455,14 @@ class GDict(ADict): # XXX _NamedDict
449
455
  '''
450
456
  return self._toTuple(Inverse10Tuple, dflt)
451
457
 
452
- def _toNAN(self, outmask): # .GeodesicLineExact._GenPosition
458
+ def _toNAN(self, outmask): # .GeodesicExact._GDistInverse, .GeodesicLineExact._GenPosition
453
459
  '''(INTERNAL) Convert this C{GDict} to all C{NAN}s.
454
460
  '''
455
- d = dict((n, NAN) for n in GeodSolve12Tuple._Names_)
456
- return self.set_(**d)._unCaps(outmask)
461
+ if (outmask & Caps.NONFINITONAN):
462
+ d = dict((k, NAN) for k, C in _key2Caps.items()
463
+ if (outmask & C) == C)
464
+ self.set_(**d)
465
+ return self
457
466
 
458
467
  @deprecated_method
459
468
  def toRhumb7Tuple(self, dflt=NAN): # PYCHOK no cover
@@ -543,7 +552,7 @@ class Inverse10Tuple(_GTuple):
543
552
  **updates) # PYCHOK indent
544
553
 
545
554
 
546
- class _kWrapped(object): # in .geodesicw
555
+ class _kWrapped(_CapsBase): # in .geodesicw
547
556
  '''(INTERNAL) Wrapper for some of I{Karney}'s U{geographiclib
548
557
  <https://PyPI.org/project/geographiclib>} classes.
549
558
  '''
@@ -554,13 +563,15 @@ class _kWrapped(object): # in .geodesicw
554
563
  <https://PyPI.org/project/geographiclib>} package is installed,
555
564
  otherwise raise a C{LazyImportError}.
556
565
  '''
557
- g = _xgeographiclib(type(self).__module__, 1, 49)
566
+ g = _xgeographiclib(type(self), 1, 49)
558
567
  from geographiclib.geodesic import Geodesic
559
568
  g.Geodesic = Geodesic
560
569
  from geographiclib.geodesicline import GeodesicLine
561
570
  g.GeodesicLine = GeodesicLine
562
571
  from geographiclib.geomath import Math
563
572
  g.Math = Math
573
+ # from geographiclib.polygonarea import PolygonArea
574
+ # g.PolygonArea = PolygonArea # see below
564
575
  return g
565
576
 
566
577
  @property_ROnce
@@ -586,6 +597,11 @@ class _kWrapped(object): # in .geodesicw
586
597
  return (_2_4_ if _K_2_4 else
587
598
  (_2_ if _K_2_0 else _1_)) if self.Math else NN
588
599
 
600
+ @property_ROnce
601
+ def _PolygonArea(self): # lazy import
602
+ from geographiclib.polygonarea import PolygonArea
603
+ return PolygonArea
604
+
589
605
  _wrapped = _kWrapped() # PYCHOK singleton, .datum, .test/base.py
590
606
 
591
607
 
@@ -830,7 +846,7 @@ def _norm180(deg): # mimick geomath.Math.AngNormalize
830
846
  return d
831
847
 
832
848
 
833
- def _polygon(geodesic, points, closed, line, wrap):
849
+ def _polygon(geodesic, points, closed, line, wrap, polar):
834
850
  '''(INTERNAL) Compute the area or perimeter of a polygon,
835
851
  using a L{GeodesicExact}, L{GeodesicSolve} or (if the
836
852
  C{geographiclib} package is installed) a C{Geodesic}
@@ -848,7 +864,7 @@ def _polygon(geodesic, points, closed, line, wrap):
848
864
  if not closed: # closed only
849
865
  raise _ValueError(closed=closed, points=_composite_)
850
866
 
851
- return points._sum1(_a_p, closed, line, wrap)
867
+ return points._sum1(_a_p, closed, line, wrap, polar)
852
868
 
853
869
  gP = geodesic.Polygon(line)
854
870
  _A = gP.AddPoint
@@ -867,7 +883,7 @@ def _polygon(geodesic, points, closed, line, wrap):
867
883
  _A(p0.lat, p0.lon)
868
884
 
869
885
  # gP.Compute returns (number_of_points, perimeter, signed area)
870
- return gP.Compute(False, True)[1 if line else 2]
886
+ return gP.Compute(reverse=False, sign=True, polar=polar)[1 if line else 2]
871
887
 
872
888
 
873
889
  try:
pygeodesy/lcc.py CHANGED
@@ -50,7 +50,7 @@ from pygeodesy.utily import atan1, degrees90, degrees180, sincos2, tanPI_2_2
50
50
  from math import atan, fabs, log, radians, sin, sqrt
51
51
 
52
52
  __all__ = _ALL_LAZY.lcc
53
- __version__ = '25.05.12'
53
+ __version__ = '25.05.26'
54
54
 
55
55
  _E0_ = 'E0'
56
56
  _N0_ = 'N0'
@@ -457,7 +457,7 @@ class Lcc(_NamedBase):
457
457
  # h=self.height, conic=self.conic,
458
458
  # name=self._name__(name))
459
459
  # args, kwds = _args_kwds(**kwds)
460
- # return self.__class__(*args, **kwds) # .classof
460
+ # return type(self)(*args, **kwds) # .classof
461
461
 
462
462
  @Property_RO
463
463
  def easting(self):
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__ = '25.05.21'
29
+ __version__ = '25.05.26'
30
30
 
31
31
  _class_ = 'class'
32
32
  _DNL_ = _NL_ * 2 # PYCHOK used!
@@ -347,7 +347,7 @@ class property_RO(_PropertyBase):
347
347
  if Clas: # overrides inst.__class__
348
348
  d = Clas[0].__dict__.get(uname, MISSING)
349
349
  else:
350
- d = getattr(inst.__class__, uname, MISSING)
350
+ d = getattr(type(inst), uname, MISSING)
351
351
  # if d is MISSING: # XXX superfluous
352
352
  # for c in type(inst).__mro__[:-1]:
353
353
  # if uname in c.__dict__:
@@ -403,7 +403,7 @@ class property_ROver(_property_RO___):
403
403
  '''
404
404
  v = self.method(inst)
405
405
  n = self.name
406
- C = inst.__class__
406
+ C = type(inst)
407
407
  for c in C.__mro__: # [:-1]
408
408
  if getattr(c, n, None) is self:
409
409
  setattr(c, n, v) # overwrite property_ROver
@@ -40,7 +40,7 @@ from pygeodesy.utily import acos1, asin1, atan2b, atan2d, degrees90, \
40
40
  from math import cos, fabs, log, sin, sqrt
41
41
 
42
42
  __all__ = _ALL_LAZY.sphericalBase
43
- __version__ = '25.05.12'
43
+ __version__ = '25.05.26'
44
44
 
45
45
 
46
46
  class CartesianSphericalBase(CartesianBase):
@@ -592,7 +592,7 @@ def _intersecant2(c, r, p, b, radius=R_M, exact=False, height=None, wrap=False):
592
592
  p = _unrollon(c, p, wrap=wrap)
593
593
  nonexact = exact is None
594
594
 
595
- if not isinstanceof(r, c.__class__, p.__class__):
595
+ if not isinstanceof(r, type(c), type(p)):
596
596
  r = Radius_(circle=r)
597
597
  elif nonexact:
598
598
  r = c.distanceTo(r, radius=radius, wrap=wrap)
@@ -601,7 +601,7 @@ def _intersecant2(c, r, p, b, radius=R_M, exact=False, height=None, wrap=False):
601
601
  else:
602
602
  raise _ValueError(exact=exact)
603
603
 
604
- if not isinstanceof(b, c.__class__, p.__class__):
604
+ if not isinstanceof(b, type(c), type(p)):
605
605
  b = Bearing(b)
606
606
  elif nonexact:
607
607
  b = p.initialBearingTo(b, wrap=wrap)