pygeodesy 25.4.8__py2.py3-none-any.whl → 25.5.5__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 (97) hide show
  1. pygeodesy/__init__.py +36 -31
  2. pygeodesy/__main__.py +3 -3
  3. pygeodesy/albers.py +29 -36
  4. pygeodesy/auxilats/_CX_4.py +2 -2
  5. pygeodesy/auxilats/_CX_6.py +2 -2
  6. pygeodesy/auxilats/_CX_8.py +2 -2
  7. pygeodesy/auxilats/_CX_Rs.py +9 -9
  8. pygeodesy/auxilats/__init__.py +3 -3
  9. pygeodesy/auxilats/__main__.py +8 -6
  10. pygeodesy/auxilats/auxAngle.py +2 -2
  11. pygeodesy/auxilats/auxLat.py +5 -5
  12. pygeodesy/auxilats/auxily.py +5 -3
  13. pygeodesy/azimuthal.py +7 -6
  14. pygeodesy/basics.py +32 -18
  15. pygeodesy/booleans.py +18 -16
  16. pygeodesy/cartesianBase.py +26 -24
  17. pygeodesy/clipy.py +11 -10
  18. pygeodesy/constants.py +11 -10
  19. pygeodesy/css.py +14 -11
  20. pygeodesy/datums.py +8 -8
  21. pygeodesy/deprecated/bases.py +2 -2
  22. pygeodesy/deprecated/classes.py +2 -2
  23. pygeodesy/deprecated/consterns.py +4 -4
  24. pygeodesy/dms.py +8 -8
  25. pygeodesy/ecef.py +22 -29
  26. pygeodesy/ecefLocals.py +186 -0
  27. pygeodesy/elevations.py +9 -8
  28. pygeodesy/ellipsoidalBase.py +19 -8
  29. pygeodesy/ellipsoidalBaseDI.py +17 -15
  30. pygeodesy/ellipsoidalNvector.py +6 -3
  31. pygeodesy/ellipsoidalVincenty.py +4 -1
  32. pygeodesy/ellipsoids.py +186 -164
  33. pygeodesy/elliptic.py +9 -9
  34. pygeodesy/errors.py +44 -43
  35. pygeodesy/etm.py +7 -7
  36. pygeodesy/fmath.py +30 -14
  37. pygeodesy/formy.py +11 -12
  38. pygeodesy/frechet.py +216 -109
  39. pygeodesy/fstats.py +5 -4
  40. pygeodesy/fsums.py +79 -78
  41. pygeodesy/gars.py +4 -3
  42. pygeodesy/geodesici.py +15 -14
  43. pygeodesy/geodesicw.py +34 -32
  44. pygeodesy/geodesicx/__init__.py +1 -1
  45. pygeodesy/geodesicx/__main__.py +11 -9
  46. pygeodesy/geodesicx/gx.py +30 -33
  47. pygeodesy/geodesicx/gxarea.py +2 -2
  48. pygeodesy/geodesicx/gxline.py +5 -5
  49. pygeodesy/geodsolve.py +18 -17
  50. pygeodesy/geohash.py +5 -5
  51. pygeodesy/geoids.py +34 -31
  52. pygeodesy/hausdorff.py +17 -13
  53. pygeodesy/heights.py +2 -4
  54. pygeodesy/internals.py +28 -44
  55. pygeodesy/interns.py +10 -7
  56. pygeodesy/iters.py +8 -8
  57. pygeodesy/karney.py +68 -62
  58. pygeodesy/ktm.py +5 -5
  59. pygeodesy/latlonBase.py +20 -21
  60. pygeodesy/lazily.py +104 -78
  61. pygeodesy/lcc.py +11 -9
  62. pygeodesy/ltp.py +56 -58
  63. pygeodesy/ltpTuples.py +35 -36
  64. pygeodesy/mgrs.py +7 -6
  65. pygeodesy/named.py +48 -177
  66. pygeodesy/nvectorBase.py +7 -7
  67. pygeodesy/osgr.py +9 -8
  68. pygeodesy/points.py +12 -10
  69. pygeodesy/props.py +25 -25
  70. pygeodesy/resections.py +83 -80
  71. pygeodesy/rhumb/__init__.py +1 -1
  72. pygeodesy/rhumb/aux_.py +7 -7
  73. pygeodesy/rhumb/bases.py +22 -20
  74. pygeodesy/rhumb/ekx.py +6 -6
  75. pygeodesy/rhumb/solve.py +15 -15
  76. pygeodesy/solveBase.py +3 -3
  77. pygeodesy/sphericalBase.py +6 -6
  78. pygeodesy/sphericalNvector.py +6 -5
  79. pygeodesy/sphericalTrigonometry.py +8 -7
  80. pygeodesy/streprs.py +14 -14
  81. pygeodesy/trf.py +14 -12
  82. pygeodesy/triaxials.py +29 -26
  83. pygeodesy/units.py +5 -4
  84. pygeodesy/unitsBase.py +5 -4
  85. pygeodesy/ups.py +3 -3
  86. pygeodesy/utily.py +4 -4
  87. pygeodesy/utmups.py +4 -4
  88. pygeodesy/utmupsBase.py +110 -18
  89. pygeodesy/vector2d.py +20 -13
  90. pygeodesy/vector3d.py +7 -6
  91. pygeodesy/webmercator.py +6 -5
  92. pygeodesy/wgrs.py +6 -5
  93. {pygeodesy-25.4.8.dist-info → pygeodesy-25.5.5.dist-info}/METADATA +30 -25
  94. pygeodesy-25.5.5.dist-info/RECORD +119 -0
  95. pygeodesy-25.4.8.dist-info/RECORD +0 -118
  96. {pygeodesy-25.4.8.dist-info → pygeodesy-25.5.5.dist-info}/WHEEL +0 -0
  97. {pygeodesy-25.4.8.dist-info → pygeodesy-25.5.5.dist-info}/top_level.txt +0 -0
pygeodesy/triaxials.py CHANGED
@@ -17,20 +17,20 @@ see the U{GeographicLib<https://GeographicLib.SourceForge.io>} documentation.
17
17
  @var Triaxials.Amalthea: Triaxial(name='Amalthea', a=125000, b=73000, c=64000, e2ab=0.658944, e2bc=0.231375493, e2ac=0.737856, volume=2446253479595252, area=93239507787.490371704, area_p=93212299402.670425415)
18
18
  @var Triaxials.Ariel: Triaxial(name='Ariel', a=581100, b=577900, c=577700, e2ab=0.01098327, e2bc=0.000692042, e2ac=0.011667711, volume=812633172614203904, area=4211301462766.580078125, area_p=4211301574065.829589844)
19
19
  @var Triaxials.Earth: Triaxial(name='Earth', a=6378173.435, b=6378103.9, c=6356754.399999999, e2ab=0.000021804, e2bc=0.006683418, e2ac=0.006705077, volume=1083208241574987694080, area=510065911057441.0625, area_p=510065915922713.6875)
20
- @var Triaxials.Enceladus: Triaxial(name='Enceladus', a=256600, b=251400, c=248300, e2ab=0.040119337, e2bc=0.024509841, e2ac=0.06364586, volume=67094551514082248, area=798618496278.596679688, area_p=798619018175.109863281)
20
+ @var Triaxials.Enceladus: Triaxial(name='Enceladus', a=256600, b=251400, c=248300, e2ab=0.040119337, e2bc=0.024509841, e2ac=0.06364586, volume=67094551514082248, area=798618496278.596679688, area_p=798619018175.109985352)
21
21
  @var Triaxials.Europa: Triaxial(name='Europa', a=1564130, b=1561230, c=1560930, e2ab=0.003704694, e2bc=0.000384275, e2ac=0.004087546, volume=15966575194402123776, area=30663773697323.51953125, area_p=30663773794562.45703125)
22
22
  @var Triaxials.Io: Triaxial(name='Io', a=1829400, b=1819300, c=1815700, e2ab=0.011011391, e2bc=0.003953651, e2ac=0.014921506, volume=25313121117889765376, area=41691875849096.7421875, area_p=41691877397441.2109375)
23
23
  @var Triaxials.Mars: Triaxial(name='Mars', a=3394600, b=3393300, c=3376300, e2ab=0.000765776, e2bc=0.009994646, e2ac=0.010752768, volume=162907283585817247744, area=144249140795107.4375, area_p=144249144150662.15625)
24
- @var Triaxials.Mimas: Triaxial(name='Mimas', a=207400, b=196800, c=190600, e2ab=0.09960581, e2bc=0.062015624, e2ac=0.155444317, volume=32587072869017956, area=493855762247.691894531, area_p=493857714107.9375)
25
- @var Triaxials.Miranda: Triaxial(name='Miranda', a=240400, b=234200, c=232900, e2ab=0.050915557, e2bc=0.011070811, e2ac=0.061422691, volume=54926187094835456, area=698880863325.756958008, area_p=698881306767.950317383)
26
- @var Triaxials.Moon: Triaxial(name='Moon', a=1735550, b=1735324, c=1734898, e2ab=0.000260419, e2bc=0.000490914, e2ac=0.000751206, volume=21886698675223740416, area=37838824729886.09375, area_p=37838824733332.2265625)
24
+ @var Triaxials.Mimas: Triaxial(name='Mimas', a=207400, b=196800, c=190600, e2ab=0.09960581, e2bc=0.062015624, e2ac=0.155444317, volume=32587072869017956, area=493855762247.691833496, area_p=493857714107.9375)
25
+ @var Triaxials.Miranda: Triaxial(name='Miranda', a=240400, b=234200, c=232900, e2ab=0.050915557, e2bc=0.011070811, e2ac=0.061422691, volume=54926187094835456, area=698880863325.757080078, area_p=698881306767.950317383)
26
+ @var Triaxials.Moon: Triaxial(name='Moon', a=1735550, b=1735324, c=1734898, e2ab=0.000260419, e2bc=0.000490914, e2ac=0.000751206, volume=21886698675223740416, area=37838824729886.09375, area_p=37838824733332.21875)
27
27
  @var Triaxials.Tethys: Triaxial(name='Tethys', a=535600, b=528200, c=525800, e2ab=0.027441672, e2bc=0.009066821, e2ac=0.036259685, volume=623086233855821440, area=3528073490771.394042969, area_p=3528074261832.738769531)
28
28
  @var Triaxials.WGS84_35: Triaxial(name='WGS84_35', a=6378172, b=6378102, c=6356752.314245179, e2ab=0.00002195, e2bc=0.006683478, e2ac=0.006705281, volume=1083207319768789942272, area=510065621722018.125, area_p=510065626587483.3125)
29
29
  '''
30
30
  # make sure int/int division yields float quotient, see .basics
31
31
  from __future__ import division as _; del _ # PYCHOK semicolon
32
32
 
33
- from pygeodesy.basics import isLatLon, isscalar
33
+ from pygeodesy.basics import _isin, isLatLon, isscalar
34
34
  from pygeodesy.constants import EPS, EPS0, EPS02, EPS4, INT0, PI2, PI_3, PI4, \
35
35
  _EPS2e4, _SQRT2_2, float0_, isfinite, isnear1, _over, \
36
36
  _0_0, _0_5, _1_0, _N_1_0, _64_0, _4_0 # PYCHOK used!
@@ -40,10 +40,10 @@ from pygeodesy.datums import Datum, _spherical_datum, _WGS84, Ellipsoid, _EWGS8
40
40
  from pygeodesy.errors import _AssertionError, _ValueError
41
41
  from pygeodesy.fmath import Fdot, fdot, fmean_, hypot, hypot_, norm2, sqrt0
42
42
  from pygeodesy.fsums import _Fsumf_, fsumf_, fsum1f_
43
- from pygeodesy.interns import NN, _a_, _b_, _beta_, _c_, _distant_, _finite_, \
44
- _height_, _inside_, _near_, _negative_, _not_, \
45
- _NOTEQUAL_, _null_, _opposite_, _outside_, _SPACE_, \
46
- _spherical_, _too_, _x_, _y_
43
+ from pygeodesy.interns import NN, _a_, _b_, _beta_, _c_, _distant_, _DMAIN_, \
44
+ _finite_, _height_, _inside_, _near_, _negative_, \
45
+ _not_, _NOTEQUAL_, _null_, _opposite_, _outside_, \
46
+ _SPACE_, _spherical_, _too_, _x_, _y_
47
47
  # from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS # from .vector3d
48
48
  from pygeodesy.named import _lazyNamedEnumItem as _lazy, _name__, _NamedEnum, \
49
49
  _NamedEnumItem, _Pass
@@ -59,7 +59,7 @@ from pygeodesy.vector3d import _otherV3d, Vector3d, _ALL_LAZY, _MODS
59
59
  from math import fabs, sqrt
60
60
 
61
61
  __all__ = _ALL_LAZY.triaxials
62
- __version__ = '24.11.24'
62
+ __version__ = '25.04.14'
63
63
 
64
64
  _not_ordered_ = _not_('ordered')
65
65
  _omega_ = 'omega'
@@ -258,7 +258,7 @@ class Triaxial_(_NamedEnumItem):
258
258
 
259
259
  @Property_RO
260
260
  def _a2_b2(self):
261
- '''(INTERNAL) Get C{(a/b)**2}.
261
+ '''(INTERNAL) Get C{(a / b)**2}.
262
262
  '''
263
263
  a, b, _ = self._abc3
264
264
  return (a / b)**2 if a != b else _1_0
@@ -321,7 +321,7 @@ class Triaxial_(_NamedEnumItem):
321
321
 
322
322
  @Property_RO
323
323
  def _c2_b2(self):
324
- '''(INTERNAL) Get C{(c/b)**2}.
324
+ '''(INTERNAL) Get C{(c / b)**2}.
325
325
  '''
326
326
  _, b, c = self._abc3
327
327
  return (c / b)**2 if b != c else _1_0
@@ -422,7 +422,7 @@ class Triaxial_(_NamedEnumItem):
422
422
  raise TriaxialError(x=x, y=y, z=z, cause=e)
423
423
  if h > 0 and self.sideOf(v, eps=EPS0) < 0:
424
424
  h = -h # inside
425
- n = _name__(name, name__=self.height4) # _DUNDER_nameof
425
+ n = _name__(name, name__=self.height4) # typename
426
426
  return Vector4Tuple(x, y, z, h, iteration=i, name=n)
427
427
 
428
428
  @Property_RO
@@ -507,7 +507,7 @@ class Triaxial_(_NamedEnumItem):
507
507
  @return: 2-Tuple C{((a, b, c), ijk)} with C{a} >= C{b} >= C{c}
508
508
  and C{ijk} a 3-tuple with the initial indices.
509
509
  '''
510
- i, j, k = 0, 1, 2 # range(3)
510
+ i, j, k = range(3)
511
511
  if a < b:
512
512
  a, b, i, j = b, a, j, i
513
513
  if a < c:
@@ -1245,9 +1245,9 @@ def hartzell4(pov, los=False, tri_biax=_WGS84, **name):
1245
1245
  '''Compute the intersection of a tri-/biaxial ellipsoid and a Line-Of-Sight from
1246
1246
  a Point-Of-View outside.
1247
1247
 
1248
- @arg pov: Point-Of-View outside the tri-/biaxial (C{Cartesian}, L{Ecef9Tuple}
1248
+ @arg pov: Point-Of-View outside the tri-/biaxial (C{Cartesian}, L{Ecef9Tuple},
1249
1249
  C{LatLon} or L{Vector3d}).
1250
- @kwarg los: Line-Of-Sight, I{direction} to the tri-/biaxial (L{Los}, L{Vector3d}),
1250
+ @kwarg los: Line-Of-Sight, I{direction} to the tri-/biaxial (L{Los}, L{Vector3d})
1251
1251
  or C{True} for the I{normal, perpendicular, plumb} to the surface of
1252
1252
  the tri-/biaxial or C{False} or C{None} to point to its center.
1253
1253
  @kwarg tri_biax: A triaxial (L{Triaxial}, L{Triaxial_}, L{JacobiConformal} or
@@ -1275,13 +1275,13 @@ def hartzell4(pov, los=False, tri_biax=_WGS84, **name):
1275
1275
  T = tri_biax
1276
1276
  else:
1277
1277
  D = tri_biax if isinstance(tri_biax, Datum) else \
1278
- _spherical_datum(tri_biax, name__=hartzell4) # _DUNDER_nameof
1278
+ _spherical_datum(tri_biax, name__=hartzell4) # typename
1279
1279
  T = D.ellipsoid._triaxial
1280
1280
  try:
1281
1281
  v, h, i = _hartzell3(pov, los, T)
1282
1282
  except Exception as x:
1283
1283
  raise TriaxialError(pov=pov, los=los, tri_biax=tri_biax, cause=x)
1284
- n = _name__(name, name__=hartzell4) # _DUNDER_nameof
1284
+ n = _name__(name, name__=hartzell4) # typename
1285
1285
  return Vector4Tuple(v.x, v.y, v.z, h, iteration=i, name=n)
1286
1286
 
1287
1287
 
@@ -1478,19 +1478,18 @@ def _rootNd(r, s, u, v, w, g, eps=EPS0):
1478
1478
  '''
1479
1479
  u *= r
1480
1480
  v *= s # 0 for 2-D root
1481
- t0 = w + _N_1_0
1482
- t1 = _0_0 if g < 0 else (hypot_(u, w, v) + _N_1_0)
1481
+ t0 = w - _1_0
1482
+ t1 = _0_0 if g < 0 else (hypot_(u, w, v) - _1_0)
1483
1483
  # assert t0 <= t1
1484
- _e = -eps
1485
- for i in range(1, _TRIPS): # 47..65
1484
+ for i in range(1, _TRIPS): # 48..58
1486
1485
  t = (t1 + t0) * _0_5
1487
1486
  e = t1 - t0
1488
- if _e < e < eps or t in (t0, t1):
1487
+ if eps > e > -eps or _isin(t, t0, t1):
1489
1488
  break
1490
1489
  g = fsumf_(_N_1_0, # ~= _hypot2_1
1491
1490
  _over02(u, t + r),
1492
- _over02(w, t - _N_1_0),
1493
- _over02(v, t + s) if v else _0_0)
1491
+ _over02(w, t + _1_0), (
1492
+ _over02(v, t + s) if v else _0_0))
1494
1493
  if g > 0:
1495
1494
  t0 = t
1496
1495
  elif g < 0:
@@ -1527,7 +1526,7 @@ def _validate(a, b, c, d, T, x, y, z, val):
1527
1526
  dot=e, eps=val)
1528
1527
 
1529
1528
 
1530
- if __name__ == '__main__':
1529
+ if __name__ == _DMAIN_:
1531
1530
 
1532
1531
  from pygeodesy import printf
1533
1532
  from pygeodesy.interns import _COMMA_, _NL_, _NLATvar_
@@ -1540,6 +1539,10 @@ if __name__ == '__main__':
1540
1539
  t = [NN] + Triaxials.toRepr(all=True, asorted=True).split(_NL_)
1541
1540
  printf(_NLATvar_.join(i.strip(_COMMA_) for i in t))
1542
1541
 
1542
+ # % python3 -m pygeodesy.triaxials
1543
+ #
1544
+ # Bektas: height4(x=3909251.554667, y=3909165.750567, z=3170432.501602, h=999.999996)
1545
+
1543
1546
  # **) MIT License
1544
1547
  #
1545
1548
  # Copyright (C) 2022-2025 -- mrJean1 at Gmail -- All Rights Reserved.
pygeodesy/units.py CHANGED
@@ -6,11 +6,12 @@ basic C{float}, C{int} respectively C{str} to named units as L{Degrees},
6
6
  L{Feet}, L{Meter}, L{Radians}, etc.
7
7
  '''
8
8
 
9
- from pygeodesy.basics import isscalar, issubclassof, signOf
9
+ from pygeodesy.basics import isscalar, issubclassof, signOf, typename
10
10
  from pygeodesy.constants import EPS, EPS1, PI, PI2, PI_2, _umod_360, _0_0, \
11
11
  _0_001, _0_5, INT0 # PYCHOK for .mgrs, .namedTuples
12
12
  from pygeodesy.dms import F__F, F__F_, S_NUL, S_SEP, parseDMS, parseRad, _toDMS
13
13
  from pygeodesy.errors import _AssertionError, TRFError, UnitError, _xattr, _xcallable
14
+ # from pygeodesy.internals import typename # from .basics
14
15
  from pygeodesy.interns import NN, _azimuth_, _band_, _bearing_, _COMMASPACE_, \
15
16
  _degrees_, _degrees2_, _distance_, _E_, _easting_, \
16
17
  _epoch_, _EW_, _feet_, _height_, _lam_, _lat_, _LatLon_, \
@@ -27,7 +28,7 @@ from pygeodesy.unitsBase import Float, Int, _NamedUnit, Radius, Str, Fmt, fstr
27
28
  from math import degrees, isnan, radians
28
29
 
29
30
  __all__ = _ALL_LAZY.units
30
- __version__ = '24.11.14'
31
+ __version__ = '25.04.14'
31
32
 
32
33
 
33
34
  class Float_(Float):
@@ -860,8 +861,8 @@ def _std_repr(*Classes):
860
861
  '''
861
862
  from pygeodesy.internals import _getenv
862
863
  for C in Classes:
863
- if hasattr(C, _std_repr.__name__): # PYCHOK del _std_repr
864
- env = 'PYGEODESY_%s_STD_REPR' % (C.__name__.upper(),)
864
+ if hasattr(C, typename(_std_repr)): # PYCHOK del _std_repr
865
+ env = 'PYGEODESY_%s_STD_REPR' % (typename(C).upper(),)
865
866
  if _getenv(env, _std_).lower() != _std_:
866
867
  C._std_repr = False
867
868
 
pygeodesy/unitsBase.py CHANGED
@@ -4,8 +4,9 @@
4
4
  u'''Basic C{Float}, C{Int} and C{Str}ing units classes.
5
5
  '''
6
6
 
7
- from pygeodesy.basics import isstr, issubclassof, _xsubclassof
7
+ from pygeodesy.basics import _isin, isstr, issubclassof, _xsubclassof, typename
8
8
  from pygeodesy.errors import _IsnotError, _UnexpectedError, UnitError, _XError
9
+ # from pygeodesy.internals import typename # from .basics
9
10
  from pygeodesy.interns import NN, _degrees_, _degrees2_, _invalid_, _meter_, \
10
11
  _radians_, _radians2_, _radius_, _UNDER_, _units_, \
11
12
  _std_ # PYCHOK used!
@@ -15,7 +16,7 @@ from pygeodesy.named import modulename, _Named, property_doc_
15
16
  from pygeodesy.streprs import Fmt, fstr
16
17
 
17
18
  __all__ = _ALL_LAZY.unitsBase
18
- __version__ = '24.08.13'
19
+ __version__ = '25.04.14'
19
20
 
20
21
 
21
22
  class _NamedUnit(_Named):
@@ -49,7 +50,7 @@ class _NamedUnit(_Named):
49
50
  elif name:
50
51
  pass
51
52
  elif name__ is not None:
52
- name = name__.__name__
53
+ name = typename(name__)
53
54
  return name, arg
54
55
 
55
56
  @staticmethod # PYCHOK unused suffix
@@ -84,7 +85,7 @@ class _NamedUnit(_Named):
84
85
  def std_repr(self, std):
85
86
  '''Set the representation (C{True} or C{"std"} for standard).
86
87
  '''
87
- self._std_repr = std in (True, _std_)
88
+ self._std_repr = _isin(std, True, _std_)
88
89
 
89
90
  def _toRepr(self, value):
90
91
  '''(INTERNAL) Representation "<name> (<value>)" or "<classname>(<value>)".
pygeodesy/ups.py CHANGED
@@ -28,7 +28,7 @@ from pygeodesy.datums import _ellipsoidal_datum, _WGS84
28
28
  from pygeodesy.dms import degDMS, _neg, parseDMS2
29
29
  from pygeodesy.errors import RangeError, _ValueError, _xkwds_pop2
30
30
  from pygeodesy.fmath import hypot, hypot1, sqrt0
31
- from pygeodesy.internals import _getPYGEODESY, _under
31
+ from pygeodesy.internals import _envPYGEODESY, _under
32
32
  from pygeodesy.interns import NN, _COMMASPACE_, _inside_, _N_, \
33
33
  _pole_, _range_, _S_, _scale0_, \
34
34
  _SPACE_, _std_, _to_, _UTM_
@@ -49,9 +49,9 @@ from pygeodesy.utmupsBase import Fmt, _LLEB, _hemi, _parseUTMUPS5, _to4lldn, \
49
49
  from math import fabs, radians, tan # as _tan
50
50
 
51
51
  __all__ = _ALL_LAZY.ups
52
- __version__ = '24.11.26'
52
+ __version__ = '25.04.14'
53
53
 
54
- _BZ_UPS = _getPYGEODESY('UPS_POLES', _std_) == _std_
54
+ _BZ_UPS = _envPYGEODESY('UPS_POLES', _std_) == _std_
55
55
  _Falsing = Meter(2000e3) # false easting and northing (C{meter})
56
56
  _K0_UPS = Float(_K0_UPS= 0.994) # scale factor at central meridian
57
57
  _K1_UPS = Float(_K1_UPS=_1_0) # rescale point scale factor
pygeodesy/utily.py CHANGED
@@ -18,7 +18,7 @@ from pygeodesy.constants import EPS, EPS0, INF, NAN, PI, PI2, PI_2, R_M, \
18
18
  _copysign_0_0, _float, _isfinite, isnan, isnear0, \
19
19
  _over, _umod_360, _umod_PI2
20
20
  from pygeodesy.errors import _ValueError, _xkwds, _ALL_LAZY, _MODS
21
- from pygeodesy.internals import _passargs
21
+ from pygeodesy.internals import _passargs, typename
22
22
  from pygeodesy.interns import _edge_, _radians_, _semi_circular_, _SPACE_
23
23
  # from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS # from .errors
24
24
  from pygeodesy.units import Degrees, Degrees_, Feet, Float, Lam, Lamd, \
@@ -28,7 +28,7 @@ from math import acos, asin, atan2 as _atan2, cos, degrees, fabs, radians, \
28
28
  sin, tan as _tan # pow
29
29
 
30
30
  __all__ = _ALL_LAZY.utily
31
- __version__ = '25.01.05'
31
+ __version__ = '25.04.14'
32
32
 
33
33
  _G_DEG = _float( 400.0 / _360_0) # grades per degree
34
34
  _G_RAD = _float( 400.0 / PI2) # grades per radian
@@ -1109,7 +1109,7 @@ def unrollPI(rad1, rad2, wrap=True):
1109
1109
  def _valueError(where, x, raiser=True, **kwds):
1110
1110
  '''(INTERNAL) Return a C{_ValueError} or C{None}.
1111
1111
  '''
1112
- t = _MODS.streprs.Fmt.PAREN(where.__name__, x)
1112
+ t = _MODS.streprs.Fmt.PAREN(typename(where), x)
1113
1113
  return _ValueError(t, **kwds) if raiser else None
1114
1114
 
1115
1115
 
@@ -1193,7 +1193,7 @@ class _Wrap(object):
1193
1193
  lat, lon = ll.latlon
1194
1194
  if fabs(lon) > _180_0 or fabs(lat) > _90_0:
1195
1195
  _n = self.latlon
1196
- ll = ll.copy(name=_n.__name__)
1196
+ ll = ll.copy(name=typename(_n))
1197
1197
  ll.latlon = _n(lat, lon)
1198
1198
  return ll
1199
1199
 
pygeodesy/utmups.py CHANGED
@@ -14,14 +14,14 @@ A pure Python implementation, partially transcoded from C++ class U{UTMUPS
14
14
  <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1UTMUPS.html>}
15
15
  by I{Charles Karney}.
16
16
  '''
17
- # from pygeodesy.basics import map1 # from .namedTuples
17
+ from pygeodesy.basics import _isin, map1
18
18
  # from pygeodesy.datums import _WGS84 # from .utmupsBase
19
19
  from pygeodesy.errors import _IsnotError, RangeError, _ValueError, _xkwds_pop2
20
20
  from pygeodesy.interns import NN, _easting_, _MGRS_, _northing_, _NS_, \
21
21
  _outside_, _range_, _SPACE_, _UPS_, _UTM_
22
22
  from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS
23
23
  from pygeodesy.named import modulename
24
- from pygeodesy.namedTuples import UtmUps5Tuple, UtmUps8Tuple, map1
24
+ from pygeodesy.namedTuples import UtmUps5Tuple, UtmUps8Tuple
25
25
  # from pygeodesy.streprs import Fmt # from .utmupsBase
26
26
  from pygeodesy.units import Northing, _100km
27
27
  from pygeodesy.ups import parseUPS5, toUps8, Ups, UPSError, upsZoneBand5
@@ -31,7 +31,7 @@ from pygeodesy.utmupsBase import Fmt, _to4lldn, _to3zBhp, _UPS_ZONE, \
31
31
  _UTMUPS_ZONE_MAX, _WGS84
32
32
 
33
33
  __all__ = _ALL_LAZY.utmups
34
- __version__ = '24.06.11'
34
+ __version__ = '25.04.14'
35
35
 
36
36
  _MGRS_TILE = _100km # in .mgrs.Mgrs.tile
37
37
 
@@ -179,7 +179,7 @@ def UtmUps(zone, hemipole, easting, northing, band=NN, datum=_WGS84, falsed=True
179
179
  <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1UTMUPS.html>}.
180
180
  '''
181
181
  z, B, hp = _to3zBhp(zone, band, hemipole=hemipole)
182
- U = Ups if z in (_UPS_ZONE, _UPS_ZONE_STR) else Utm
182
+ U = Ups if _isin(z, _UPS_ZONE, _UPS_ZONE_STR) else Utm
183
183
  return U(z, hp, easting, northing, band=B, datum=datum, falsed=falsed, **name)
184
184
 
185
185
 
pygeodesy/utmupsBase.py CHANGED
@@ -4,39 +4,44 @@
4
4
  u'''(INTERNAL) Private class C{UtmUpsBase}, functions and constants
5
5
  for modules L{epsg}, L{etm}, L{mgrs}, L{ups} and L{utm}.
6
6
  '''
7
+ # make sure int/int division yields float quotient, see .basics
8
+ from __future__ import division as _; del _ # PYCHOK semicolon
7
9
 
8
- from pygeodesy.basics import isint, isscalar, isstr, neg_, \
9
- _xinstanceof, _xsubclassof
10
+ from pygeodesy.basics import _copysign, _isin, isint, isscalar, isstr, \
11
+ neg_, _xinstanceof, _xsubclassof
10
12
  from pygeodesy.constants import _float, _0_0, _0_5, _N_90_0, _180_0
11
13
  from pygeodesy.datums import _ellipsoidal_datum, _WGS84
12
14
  from pygeodesy.dms import degDMS, parseDMS2
13
15
  from pygeodesy.ellipsoidalBase import LatLonEllipsoidalBase as _LLEB
14
16
  from pygeodesy.errors import _or, ParseError, _parseX, _ValueError, \
15
17
  _xattrs, _xkwds, _xkwds_not
18
+ # from pygeodesy.fsums import Fsum # _MODS
16
19
  # from pygeodesy.internals import _name__, _under # from .named
17
- from pygeodesy.interns import NN, _A_, _B_, _COMMA_, _Error_, \
18
- _gamma_, _n_a_, _not_, _N_, _NS_, _PLUS_, \
19
- _S_, _scale_, _SPACE_, _Y_, _Z_
20
+ from pygeodesy.interns import NN, _A_, _B_, _COMMA_, _Error_, _gamma_, \
21
+ _n_a_, _not_, _N_, _NS_, _PLUS_, _scale_, \
22
+ _S_, _SPACE_, _Y_, _Z_
20
23
  from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS
21
24
  from pygeodesy.named import _name__, _NamedBase, _under
22
25
  from pygeodesy.namedTuples import EasNor2Tuple, LatLonDatum5Tuple
23
26
  from pygeodesy.props import deprecated_method, property_doc_, _update_all, \
24
27
  deprecated_property_RO, Property_RO, property_RO
25
28
  from pygeodesy.streprs import Fmt, fstr, _fstrENH2, _xzipairs
26
- from pygeodesy.units import Band, Easting, Northing, Scalar, Zone
27
- from pygeodesy.utily import _Wrap, wrap360
29
+ from pygeodesy.units import Band, Easting, Lat, Northing, Phi, Scalar, Zone
30
+ from pygeodesy.utily import atan1, _Wrap, wrap360
31
+
32
+ from math import cos, degrees, fabs, sin, tan # copysign as _copysign
28
33
 
29
34
  __all__ = _ALL_LAZY.utmupsBase
30
- __version__ = '24.08.13'
35
+ __version__ = '25.04.26'
31
36
 
32
37
  _UPS_BANDS = _A_, _B_, _Y_, _Z_ # UPS polar bands SE, SW, NE, NW
33
38
  # _UTM_BANDS = _MODS.utm._Bands
34
39
 
35
- _UTM_LAT_MAX = _float( 84) # PYCHOK for export (C{degrees})
36
- _UTM_LAT_MIN = _float(-80) # PYCHOK for export (C{degrees})
40
+ _UTM_LAT_MAX = _float( 84) # PYCHOK for export (C{degrees})
41
+ _UTM_LAT_MIN = _float(-80) # PYCHOK for export (C{degrees})
37
42
 
38
- _UPS_LAT_MAX = _UTM_LAT_MAX - _0_5 # PYCHOK includes 30' UTM overlap
39
- _UPS_LAT_MIN = _UTM_LAT_MIN + _0_5 # PYCHOK includes 30' UTM overlap
43
+ _UPS_LAT_MAX = _UTM_LAT_MAX - _0_5 # PYCHOK includes 30' UTM overlap
44
+ _UPS_LAT_MIN = _UTM_LAT_MIN + _0_5 # PYCHOK includes 30' UTM overlap
40
45
 
41
46
  _UPS_LATS = {_A_: _N_90_0, _Y_: _UTM_LAT_MAX, # UPS band bottom latitudes,
42
47
  _B_: _N_90_0, _Z_: _UTM_LAT_MAX} # PYCHOK see .Mgrs.bandLatitude
@@ -91,7 +96,7 @@ class UtmUpsBase(_NamedBase):
91
96
  if band:
92
97
  self._band1(band)
93
98
 
94
- if datum not in (None, self._datum):
99
+ if not _isin(datum, None, self._datum):
95
100
  self._datum = _ellipsoidal_datum(datum) # raiser=_datum_, name=band
96
101
 
97
102
  if not falsed:
@@ -189,6 +194,67 @@ class UtmUpsBase(_NamedBase):
189
194
  '''I{Must be overloaded}.'''
190
195
  self._notOverloaded(self)
191
196
 
197
+ def _footpoint(self, y, lat0, makris):
198
+ '''(INTERNAL) Return the foot-point latitude in C{radians}.
199
+ '''
200
+ F = _MODS.fsums.Fsum
201
+ E = self.datum.ellipsoid
202
+ if y is None:
203
+ _, y = self.eastingnorthing2(falsed=False)
204
+ B = F(E.Llat(lat0), y)
205
+ if E.isSpherical:
206
+ r = B.fover(E.a) # == E.b
207
+
208
+ elif makris:
209
+ b = B.fover(E.b)
210
+ r = fabs(b)
211
+ if r:
212
+ e2 = E.e22 # E.e22abs?
213
+ e4 = E.e4
214
+
215
+ e1 = F(-1, e2 / 4, -11 / 64 * e4).as_iscalar
216
+ e2 = F( e2 / 8, -13 / 128 * e4).as_iscalar
217
+ e4 *= cos(r)**2 / 8
218
+
219
+ s = sin(r * 2)
220
+ r = -r
221
+ U = F(e1 * r, e2 * s, e4 * r, e4 / 8 * 5 * s**2)
222
+ r = _copysign(atan1(E.a * tan(float(U)) / E.b), b)
223
+
224
+ # elif clins: # APRIL-ZJU/clins/include/utils/gps_convert_utils.h
225
+ # n = E.n
226
+ # n2 = n**2
227
+ # n3 = n**3
228
+ # n4 = n**4
229
+ # n5 = n**5
230
+ # A = F(1, n2 / 4, n4 / 64).fmul((E.a + E.b) / 2)
231
+ # r = B.fover(A)
232
+ # R = F(r)
233
+ # if clins: # FootpointLatitude, GPS-Theory-Practice, 1994
234
+ # R += F(3 / 2 * n, -27 / 32 * n3, 269 / 512 * n5).fmul(sin(r * 2))
235
+ # R += F( 21 / 16 * n2, -55 / 32 * n4).fmul(sin(r * 4))
236
+ # R += F( 151 / 96 * n3, -417 / 128 * n5).fmul(sin(r * 6))
237
+ # R += (1097 / 512 * n4) * sin(r * 8)
238
+ # else: # GPS-Theory-Practice, 1992, page 234-235
239
+ # R += F(-3 / 2 * n, 9 / 16 * n3, -3 / 32 * n5).fmul(sin(r * 2))
240
+ # R += F( 15 / 16 * n2, -15 / 32 * n4).fmul(sin(r * 4))
241
+ # R += F( -35 / 48 * n3, 105 / 256 * n4).fmul(sin(r * 6)) # n5?
242
+ # r = float(R)
243
+
244
+ else: # PyGeodetics/src/geodetics/footpoint_latitude.py
245
+ f = E.f
246
+ f2 = f**2
247
+ f3 = f**3
248
+ B0 = F(1, -f / 2, f2 / 16, f3 / 32).fmul(E.a)
249
+ r = B.fover(B0)
250
+ R = F(r)
251
+ R += F(3 / 4 * f, 3 / 8 * f2, 21 / 256 * f3).fmul(sin(r * 2))
252
+ R += F( 21 / 64 * f2, 21 / 64 * f3).fmul(sin(r * 4))
253
+ R += (151 / 768 * f3) * sin(r * 6)
254
+ r = float(R)
255
+
256
+ return r
257
+
192
258
  @Property_RO
193
259
  def gamma(self):
194
260
  '''Get the meridian convergence (C{degrees}) or C{None}
@@ -204,6 +270,23 @@ class UtmUpsBase(_NamedBase):
204
270
  self._toLLEB()
205
271
  return self._hemisphere
206
272
 
273
+ def latFootPoint(self, northing=None, lat0=0, makris=False):
274
+ '''Compute the foot-point latitude in C{degrees}.
275
+
276
+ @arg northing: Northing (C{meter}, same units this ellipsoid's axes),
277
+ overriding this northing, I{unfalsed}.
278
+ @kwarg lat0: Geodetic latitude of the meridian's origin (C{degrees}).
279
+ @kwarg makris: If C{True}, use C{Makris}' formula, otherwise C{PyGeodetics}'.
280
+
281
+ @return: Foot-point latitude (C{degrees}).
282
+
283
+ @see: U{PyGeodetics<https://GitHub.com/paarnes/pygeodetics>}, U{FootpointLatitude
284
+ <https://GitHub.com/APRIL-ZJU/clins/blob/master/include/utils/gps_convert_utils.h#L143>},
285
+ U{Makris<https://www.TandFonline.com/doi/abs/10.1179/sre.1982.26.205.345>} and
286
+ U{Geomatics' Mercator, page 60<https://Geomatics.CC/legacy-files/mercator.pdf>}.
287
+ '''
288
+ return Lat(FootPoint=degrees(self._footpoint(northing, lat0, makris)))
289
+
207
290
  def _latlon5(self, LatLon, **LatLon_kwds):
208
291
  '''(INTERNAL) Get cached C{._toLLEB} as B{C{LatLon}} instance.
209
292
  '''
@@ -255,6 +338,15 @@ class UtmUpsBase(_NamedBase):
255
338
  '''
256
339
  return self._northing
257
340
 
341
+ def phiFootPoint(self, northing=None, lat0=0, makris=False):
342
+ '''Compute the foot-point latitude in C{radians}.
343
+
344
+ @return: Foot-point latitude (C{radians}).
345
+
346
+ @see: Method L{latFootPoint<UtmUpsBase.latFootPoint>} for further details.
347
+ '''
348
+ return Phi(FootPoint=self._footpoint(northing, lat0, makris))
349
+
258
350
  @Property_RO
259
351
  def scale(self):
260
352
  '''Get the grid scale (C{float}) or C{None}.
@@ -302,9 +394,9 @@ class UtmUpsBase(_NamedBase):
302
394
  @note: If not specified, the I{latitudinal} C{band} is computed from
303
395
  the (geodetic) latitude and the C{datum}.
304
396
  '''
305
- return self._mgrs if center in (False, 0, _0_0) else (
306
- self._mgrs_lowerleft if center in (True,) else
307
- _toMgrs(_lowerleft(self, center))) # PYCHOK indent
397
+ return self._mgrs_lowerleft if center is True else (
398
+ _toMgrs(_lowerleft(self, center)) if center else
399
+ self._mgrs) # PYCHOK indent
308
400
 
309
401
  def _toRepr(self, fmt, B, cs, prec, sep): # PYCHOK expected
310
402
  '''(INTERNAL) Return a representation for this ETM/UTM/UPS coordinate.
@@ -349,8 +441,8 @@ def _lowerleft(utmups, center): # in .ellipsoidalBase._lowerleft
349
441
  t = c * 2
350
442
  e = int(utmups.easting % t)
351
443
  n = int(utmups.northing % t)
352
- if (e == c and n in (c, c - 1)) or \
353
- (n == c and e in (c, c - 1)):
444
+ if (e == c and _isin(n, c, c - 1)) or \
445
+ (n == c and _isin(e, c, c - 1)):
354
446
  break
355
447
  else:
356
448
  return utmups # unchanged
pygeodesy/vector2d.py CHANGED
@@ -2,18 +2,23 @@
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
4
  u'''2- or 3-D vectorial functions L{circin6}, L{circum3}, L{circum4_},
5
- L{iscolinearWith}, L{meeus2}, L{nearestOn}, L{radii11}, L{soddy4} and
6
- L{trilaterate2d2}.
5
+ L{iscolinearWith}, L{meeus2}, L{nearestOn}, L{radii11}, L{soddy4},
6
+ L{triaxum5} and L{trilaterate2d2}.
7
+
8
+ @note: Functions L{circin6}, L{circum3}, L{circum4_}, L{soddy4} and
9
+ L{triaxum5} require U{numpyhttps://PyPI.org/project/numpy>}
10
+ version 1.10 or newer to be installed.
7
11
  '''
8
12
 
9
- from pygeodesy.basics import len2, map2, _xnumpy
13
+ from pygeodesy.basics import len2, map2, _xnumpy, typename
10
14
  from pygeodesy.constants import EPS, EPS0, EPS02, EPS4, INF, INT0, \
11
15
  _EPS4e8, isnear0, _0_0, _0_25, _0_5, _N_0_5, \
12
16
  _1_0, _1_0_1T, _N_1_0, _2_0, _N_2_0, _4_0
13
17
  from pygeodesy.errors import _and, _AssertionError, IntersectionError, NumPyError, \
14
18
  PointsError, TriangleError, _xError, _xkwds
15
- from pygeodesy.fmath import fabs, fdot, fdot_, hypot, hypot2_, sqrt
19
+ from pygeodesy.fmath import fabs, fdot, Fdot_, fdot_, hypot, hypot2_, sqrt
16
20
  from pygeodesy.fsums import _Fsumf_, fsumf_, fsum1f_
21
+ # from pygeodesy.internals import typename # from .basics
17
22
  from pygeodesy.interns import NN, _a_, _and_, _b_, _c_, _center_, _coincident_, \
18
23
  _colinear_, _COMMASPACE_, _concentric_, _few_, \
19
24
  _intersection_, _invalid_, _near_, _no_, _of_, \
@@ -31,7 +36,7 @@ from contextlib import contextmanager
31
36
  # from math import fabs, sqrt # from .fmath
32
37
 
33
38
  __all__ = _ALL_LAZY.vector2d
34
- __version__ = '24.11.21'
39
+ __version__ = '25.04.30'
35
40
 
36
41
  _cA_ = 'cA'
37
42
  _cB_ = 'cB'
@@ -184,7 +189,7 @@ def _circin6(point1, point2, point3, eps=EPS4, useZ=True, dLL3=False, **Vector_k
184
189
 
185
190
  r = t.rIn
186
191
  c, d = _tricenter3d2(c1, r, c2, r, c3, r, eps=eps, useZ=useZ, dLL3=dLL3,
187
- **_xkwds(Vector_kwds, Vector=V, name=circin6.__name__))
192
+ **_xkwds(Vector_kwds, Vector=V, name=typename(circin6)))
188
193
  return Circin6Tuple(r, c, d, cA, cB, cC)
189
194
 
190
195
 
@@ -236,7 +241,7 @@ def _circum3(p1, point2, point3, circum=True, eps=EPS4, useZ=True, dLL3=False,
236
241
  r, d, p2, p3 = _meeus4(p1, point2, point3, circum=circum, useZ=useZ,
237
242
  clas=clas, **clas_kwds)
238
243
  if d is None: # Meeus' Type II or circum=True
239
- kwds = _xkwds(clas_kwds, eps=eps, Vector=clas, name=circum3.__name__)
244
+ kwds = _xkwds(clas_kwds, eps=eps, Vector=clas, name=typename(circum3))
240
245
  c, d = _tricenter3d2(p1, r, p2, r, p3, r, useZ=useZ, dLL3=dLL3, **kwds)
241
246
  else: # Meeus' Type I
242
247
  c, d = d, None
@@ -386,7 +391,7 @@ def _meeus4(A, point2, point3, circum=False, useZ=True, clas=None, **clas_kwds):
386
391
  r = sqrt(a * _0_25) if a > EPS02 else INT0
387
392
  t = B.plus(C).times(_0_5) # Meeus' Type I
388
393
  if clas is not None:
389
- t = clas(t.x, t.y, t.z, **_xkwds(clas_kwds, name=meeus2.__name__))
394
+ t = clas(t.x, t.y, t.z, **_xkwds(clas_kwds, name=typename(meeus2)))
390
395
  return r, t, p2, p3
391
396
 
392
397
 
@@ -410,6 +415,8 @@ class _numpy(object): # see also .formy._idllmn6, .geodesicw._wargs, .latlonBas
410
415
 
411
416
  @Property_RO
412
417
  def array(self):
418
+ '''Get U{numpy.array<https://NumPy.org/doc/2.2/reference/generated/numpy.array.html#numpy.array>}.
419
+ '''
413
420
  return self.np.array
414
421
 
415
422
  def least_squares3(self, A, b):
@@ -595,7 +602,7 @@ def soddy4(point1, point2, point3, eps=EPS4, useZ=True):
595
602
  c, d = _tricenter3d2(p1, t.rA + r,
596
603
  p2, t.rB + r,
597
604
  p3, t.rC + r, eps=eps, useZ=useZ,
598
- Vector=point1.classof, name=soddy4.__name__)
605
+ Vector=point1.classof, name=typename(soddy4))
599
606
  return Soddy4Tuple(r, c, d, t.roS)
600
607
 
601
608
 
@@ -748,8 +755,8 @@ def _trilaterate2d2(x1, y1, radius1, x2, y2, radius2, x3, y3, radius3,
748
755
  raise IntersectionError(_and(_astr(x1=x1, y1=y1, radius1=r1),
749
756
  _astr(x2=x2, y2=y2, radius2=r2),
750
757
  _astr(x3=x3, y3=y3, radius3=r3)), txt=t)
751
- t = Vector2Tuple(fdot_(c, e, -b, f) / q,
752
- fdot_(a, f, -c, d) / q, name=trilaterate2d2.__name__)
758
+ t = Vector2Tuple(Fdot_(c, e, -b, f).fover(q),
759
+ Fdot_(a, f, -c, d).fover(q), name=typename(trilaterate2d2))
753
760
 
754
761
  if eps and eps > 0: # check distances to center vs radius
755
762
  for x, y, r in ((x1, y1, r1), (x2, y2, r2), (x3, y3, r3)):
@@ -784,7 +791,7 @@ def _trilaterate3d2(c1, r1, c2, r2, c3, r3, eps=EPS4, coin=False, # MCCABE 13
784
791
  def _N3(t01, x, z):
785
792
  # compute x, y and z and return as B{C{clas}} or B{C{Vector}}
786
793
  v = x.plus(z.times(t01))
787
- n = trilaterate3d2.__name__
794
+ n = typename(trilaterate3d2)
788
795
  return _nVc(v, **_xkwds(clas_Vector_and_kwds, name=n))
789
796
 
790
797
  c2 = _otherV3d(center2=c2, NN_OK=False)
@@ -828,7 +835,7 @@ def _trilaterate3d2(c1, r1, c2, r2, c3, r3, eps=EPS4, coin=False, # MCCABE 13
828
835
  r = repr(r1) if r1 == r2 == r3 else _reprs(r1, r2, r3)
829
836
  t = _SPACE_(t, _of_, _reprs(c1, c2, c3), _with_, _radius_, r)
830
837
  elif Z is None:
831
- t = _COMMASPACE_(t, _no_(_numpy.null_space2.__name__))
838
+ t = _COMMASPACE_(t, _no_(typename(_numpy.null_space2)))
832
839
  return t
833
840
 
834
841
  # coincident, concentric, colinear, too distant, no intersection: