pygeodesy 25.4.25__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.
pygeodesy/named.py CHANGED
@@ -28,16 +28,14 @@ from pygeodesy.interns import MISSING, NN, _AT_, _COLON_, _COLONSPACE_, _COMMA_,
28
28
  _EQUAL_, _exists_, _immutable_, _name_, _NL_, _NN_, \
29
29
  _no_, _other_, _s_, _SPACE_, _std_, _UNDER_, _vs_
30
30
  from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS
31
- # from pygeodesy.ltp import Ltp, _toLocal, _toLtp # _MODS
32
- # from pygeodesy.ltpTuples import Aer, Enu, Ned # _MODS
33
31
  from pygeodesy.props import _allPropertiesOf_n, deprecated_method, _hasProperty, \
34
32
  _update_all, property_doc_, Property_RO, property_RO, \
35
- _update_attrs, property_ROver
33
+ _update_attrs
36
34
  from pygeodesy.streprs import attrs, Fmt, lrstrip, pairs, reprs, unstr
37
35
  # from pygeodesy.units import _toUnit # _MODS
38
36
 
39
37
  __all__ = _ALL_LAZY.named
40
- __version__ = '25.04.14'
38
+ __version__ = '25.04.28'
41
39
 
42
40
  _COMMANL_ = _COMMA_ + _NL_
43
41
  _COMMASPACEDOT_ = _COMMASPACE_ + _DOT_
@@ -915,148 +913,6 @@ class _NamedEnumItem(_NamedBase):
915
913
  raise _AssertionError(t)
916
914
 
917
915
 
918
- class _NamedLocal(object):
919
- '''(INTERNAL) Base class for C{CartesianBase}, C{Ecef9Tuple} and C{LatLonBase}.
920
- '''
921
-
922
- @property_ROver
923
- def Ecef(self):
924
- '''Get the ECEF I{class} (L{EcefKarney}), I{once}.
925
- '''
926
- return _MODS.ecef.EcefKarney
927
-
928
- @property_RO
929
- def _ecef9(self):
930
- '''I{Must be overloaded}.'''
931
- notOverloaded(self)
932
-
933
- @Property_RO
934
- def _Ltp(self):
935
- '''(INTERNAL) Cache this instance' LTP (L{Ltp}).
936
- '''
937
- return self._ltp.Ltp(self._ecef9, ecef=self.Ecef(self.datum), name=self.name)
938
-
939
- @property_ROver
940
- def _ltp(self):
941
- '''(INTERNAL) Get module L{pygeodesy.ltp}, I{once}.
942
- '''
943
- return _MODS.ltp
944
-
945
- def _ltp_toLocal(self, ltp, Xyz_kwds, **Xyz): # overloaded in C{Ecef9Tuple}
946
- '''(INTERNAL) Invoke C{ltp._toLocal}.
947
- '''
948
- Xyz_ = self._ltp_toLocal2(Xyz_kwds, **Xyz)
949
- return self._ltp._toLocal(self, ltp, *Xyz_) # self._ecef9
950
-
951
- def _ltp_toLocal2(self, Xyz_kwds, _None=None, **Xyz):
952
- '''(INTERNAL) Return 2-tuple C{(Xyz_Class, Xyz_kwds)}.
953
- '''
954
- C, _ = Xyz_ = _xkwds_pop2(Xyz_kwds, **Xyz)
955
- if C is not _None: # validate C
956
- n, X = _xkwds_item2(Xyz)
957
- if X is not C:
958
- _xsubclassof(X, **{n: C})
959
- return Xyz_
960
-
961
- @property_ROver
962
- def _ltpTuples(self):
963
- '''(INTERNAL) Get module L{pygeodesy.ltpTuples}, I{once}.
964
- '''
965
- return _MODS.ltpTuples
966
-
967
- def toAer(self, ltp=None, **Aer_and_kwds):
968
- '''Convert this instance to I{local} I{Azimuth, Elevation, slant Range} (AER) components.
969
-
970
- @kwarg ltp: The I{local tangent plane} (LTP) to use (L{Ltp}), overriding this
971
- instance' L{LTP<pygeodesy.named._NamedLocal.toLtp>}.
972
- @kwarg Aer_and_kwds: Optional AER class C{B{Aer}=}L{Aer<pygeodesy.ltpTuples.Aer>}
973
- to use and optionally, additional B{C{Aer}} keyword arguments.
974
-
975
- @return: An B{C{Aer}} instance.
976
-
977
- @raise TypeError: Invalid B{C{ltp}}.
978
-
979
- @see: Method L{toLocal<pygeodesy.named._NamedLocal.toLocal>}.
980
- '''
981
- return self._ltp_toLocal(ltp, Aer_and_kwds, Aer=self._ltpTuples.Aer)
982
-
983
- def toEnu(self, ltp=None, **Enu_and_kwds):
984
- '''Convert this instance to I{local} I{East, North, Up} (ENU) components.
985
-
986
- @kwarg ltp: The I{local tangent plane} (LTP) to use (L{Ltp}), overriding this
987
- instance' L{LTP<pygeodesy.named._NamedLocal.toLtp>}.
988
- @kwarg Enu_and_kwds: Optional ENU class C{B{Enu}=}L{Enu<pygeodesy.ltpTuples.Enu>}
989
- to use and optionally, additional B{C{Enu}} keyword arguments.
990
-
991
- @return: An B{C{Enu}} instance.
992
-
993
- @raise TypeError: Invalid B{C{ltp}}.
994
-
995
- @see: Method L{toLocal<pygeodesy.named._NamedLocal.toLocal>}.
996
- '''
997
- return self._ltp_toLocal(ltp, Enu_and_kwds, Enu=self._ltpTuples.Enu)
998
-
999
- def toLocal(self, Xyz=None, ltp=None, **Xyz_kwds):
1000
- '''Convert this instance to I{local} components in a I{local tangent plane} (LTP)
1001
-
1002
- @kwarg Xyz: Optional I{local} components class (L{XyzLocal}, L{Aer},
1003
- L{Enu}, L{Ned}) or C{None}.
1004
- @kwarg ltp: The I{local tangent plane} (LTP) to use (L{Ltp}), overriding this
1005
- cartesian's L{LTP<pygeodesy.named._NamedLocal.toLtp>}.
1006
- @kwarg Xyz_kwds: Optionally, additional B{C{Xyz}} keyword arguments, ignored
1007
- if C{B{Xyz} is None}.
1008
-
1009
- @return: An B{C{Xyz}} instance or a L{Local9Tuple}C{(x, y, z, lat, lon,
1010
- height, ltp, ecef, M)} if C{B{Xyz} is None} (with C{M=None}).
1011
-
1012
- @raise TypeError: Invalid B{C{ltp}}.
1013
- '''
1014
- return self._ltp_toLocal(ltp, Xyz_kwds, Xyz=Xyz, _None=Xyz)
1015
-
1016
- def toLtp(self, Ecef=None, **name):
1017
- '''Return the I{local tangent plane} (LTP) for this instance.
1018
-
1019
- @kwarg Ecef: Optional ECEF I{class} (L{EcefKarney}, ... L{EcefYou}), overriding
1020
- this instance' L{Ecef<pygeodesy.named._NamedLocal.Ecef>}.
1021
- @kwarg name: Optional C{B{name}=NN} (C{str}).
1022
-
1023
- @return: An B{C{Ltp}} instance.
1024
- '''
1025
- return self._ltp._toLtp(self, Ecef, self._ecef9, name) # needs self.Ecef and self._Ltp
1026
-
1027
- def toNed(self, ltp=None, **Ned_and_kwds):
1028
- '''Convert this instance to I{local} I{North, East, Down} (NED) components.
1029
-
1030
- @kwarg ltp: The I{local tangent plane} (LTP) to use (L{Ltp}), overriding this
1031
- instance' L{LTP<pygeodesy.named._NamedLocal.toLtp>}.
1032
- @kwarg Ned_and_kwds: Optional NED class C{B{Ned}=}L{Ned<pygeodesy.ltpTuples.Ned>}
1033
- to use and optionally, additional B{C{Ned}} keyword arguments.
1034
-
1035
- @return: An B{C{Ned}} instance.
1036
-
1037
- @raise TypeError: Invalid B{C{ltp}}.
1038
-
1039
- @see: Method L{toLocal<pygeodesy.named._NamedLocal.toLocal>}.
1040
- '''
1041
- return self._ltp_toLocal(ltp, Ned_and_kwds, Ned=self._ltpTuples.Ned)
1042
-
1043
- def toXyz(self, ltp=None, **Xyz_and_kwds):
1044
- '''Convert this instance to I{local} I{X, Y, Z} (XYZ) components.
1045
-
1046
- @kwarg ltp: The I{local tangent plane} (LTP) to use (L{Ltp}), overriding this
1047
- instance' L{LTP<pygeodesy.named._NamedLocal.toLtp>}.
1048
- @kwarg Xyz_and_kwds: Optional XYZ class C{B{Xyz}=}L{Xyz<pygeodesy.ltpTuples.XyzLocal>}
1049
- to use and optionally, additional B{C{Xyz}} keyword arguments.
1050
-
1051
- @return: An B{C{Xyz}} instance.
1052
-
1053
- @raise TypeError: Invalid B{C{ltp}}.
1054
-
1055
- @see: Method L{toLocal<pygeodesy.named._NamedLocal.toLocal>}.
1056
- '''
1057
- return self._ltp_toLocal(ltp, Xyz_and_kwds, Xyz=self._ltpTuples.XyzLocal)
1058
-
1059
-
1060
916
  # from pygeodesy.props import _NamedProperty
1061
917
 
1062
918
 
@@ -1611,7 +1467,6 @@ def _xvalid(name, underOK=False):
1611
1467
  __all__ += _ALL_DOCS(_Named,
1612
1468
  _NamedBase, # _NamedDict,
1613
1469
  _NamedEnum, _NamedEnumItem,
1614
- _NamedLocal,
1615
1470
  _NamedTuple)
1616
1471
 
1617
1472
  # **) MIT License
pygeodesy/resections.py CHANGED
@@ -18,12 +18,11 @@ from pygeodesy.constants import EPS, EPS0, EPS02, INT0, PI, PI2, PI_2, PI_4, \
18
18
  _16_0, _180_0, _360_0, isnear0, _over, _umod_360
19
19
  from pygeodesy.errors import _and, _or, TriangleError, _ValueError, _xcallable, \
20
20
  _xkwds, _xkwds_pop2
21
- from pygeodesy.fmath import favg, Fdot, fidw, fmean, hypot, hypot2_
21
+ from pygeodesy.fmath import favg, Fdot, Fdot_, fidw, _fma, fmean, hypot, hypot2_
22
22
  from pygeodesy.fsums import _Fsumf_, fsumf_, fsum1, fsum1f_
23
23
  # from pygeodesy.internals import typename # from .basics
24
24
  from pygeodesy.interns import _a_, _A_, _area_, _b_, _B_, _c_, _C_, _coincident_, \
25
- _colinear_, _d_, _invalid_, _negative_, _not_, \
26
- _rIn_, _SPACE_
25
+ _colinear_, _d_, _invalid_, _negative_, _rIn_, _SPACE_
27
26
  # from pygeodesy.lazily import _ALL_LAZY # from .basics
28
27
  from pygeodesy.named import _NamedTuple, _Pass, Fmt
29
28
  # from pygeodesy.streprs import Fmt # from .named
@@ -35,7 +34,7 @@ from pygeodesy.vector3d import _otherV3d, Vector3d
35
34
  from math import cos, degrees, fabs, radians, sin, sqrt
36
35
 
37
36
  __all__ = _ALL_LAZY.resections
38
- __version__ = '25.04.14'
37
+ __version__ = '25.05.04'
39
38
 
40
39
  _concyclic_ = 'concyclic'
41
40
  _PA_ = 'PA'
@@ -43,7 +42,6 @@ _PB_ = 'PB'
43
42
  _PC_ = 'PC'
44
43
  _pointH_ = 'pointH'
45
44
  _pointP_ = 'pointP'
46
- _positive_ = 'positive'
47
45
  _radA_ = 'radA'
48
46
  _radB_ = 'radB'
49
47
  _radC_ = 'radC'
@@ -170,11 +168,9 @@ def cassini(pointA, pointB, pointC, alpha, beta, useZ=False, **Clas_and_kwds):
170
168
 
171
169
  A, B, C = _ABC3(useZ, pointA, pointB, pointC)
172
170
  try:
173
- sa, sb = map1(float, alpha, beta)
174
- if min(sa, sb) < 0:
175
- raise ValueError(_negative_)
171
+ sa, sb = _noneg(alpha, beta)
176
172
  if fsumf_(_360_0, -sa, -sb) < EPS0:
177
- raise ValueError()
173
+ raise ValueError(_colinear_)
178
174
 
179
175
  x1, y1 = _H(A, C, sa)
180
176
  x2, y2 = _H(B, C, -sb)
@@ -253,15 +249,15 @@ def collins5(pointA, pointB, pointC, alpha, beta, useZ=False, **Clas_and_kwds):
253
249
 
254
250
  def _xyz(d, r, A, B, C, useZ):
255
251
  s, c = sincos2(r)
256
- x = d * s + A.x # fma(d, s, A.x)
257
- y = d * c + A.y # fma(d, c, A.y)
252
+ x = _fma(d, s, A.x)
253
+ y = _fma(d, c, A.y)
258
254
  z = _zidw(x, y, useZ, A, B, C)
259
255
  return x, y, z
260
256
 
261
257
  A, B, C = _ABC3(useZ, pointA, pointB, pointC)
262
258
  try:
263
- ra, rb = radians(alpha), radians(beta)
264
- if min(ra, rb) < 0:
259
+ ra, rb = t = radians(alpha), radians(beta)
260
+ if min(t) < 0:
265
261
  raise ValueError(_negative_)
266
262
 
267
263
  sra, srH = sin(ra), sin(ra + rb - PI) # rH = PI - ((PI - ra) + (PI - rb))
@@ -294,6 +290,15 @@ def collins5(pointA, pointB, pointC, alpha, beta, useZ=False, **Clas_and_kwds):
294
290
  alpha=alpha, beta=beta, cause=x)
295
291
 
296
292
 
293
+ def _noneg(*xs):
294
+ '''(INTERNAL) Return non-negative C{float}s.
295
+ '''
296
+ xs = tuple(map(float, xs))
297
+ if min(xs) < 0:
298
+ raise ValueError(_negative_)
299
+ return xs
300
+
301
+
297
302
  def pierlot(point1, point2, point3, alpha12, alpha23, useZ=False, eps=EPS,
298
303
  **Clas_and_kwds):
299
304
  '''3-Point resection using U{Pierlot<http://www.Telecom.ULg.ac.BE/publi/publications/
@@ -342,7 +347,7 @@ def pierlot(point1, point2, point3, alpha12, alpha23, useZ=False, eps=EPS,
342
347
  if eps > 0:
343
348
  return c / (min(s, -eps) if s < 0 else max(s, eps))
344
349
  t = Fmt.PARENSPACED(eps=eps)
345
- raise ValueError(_SPACE_(t, _not_, _positive_))
350
+ raise ValueError(_SPACE_(t, _invalid_))
346
351
 
347
352
  B1, B2, B3 = _B3(useZ, point1, point2, point3)
348
353
  try:
@@ -400,8 +405,8 @@ def _pierlot3(B1, B2, B3, a12, a23, useZ, _cot):
400
405
  # = (x31 - x23) * (y12 - y23) - (x12 - x23) * (y31 - y23)
401
406
  # x = (d * B2.x + K * Y12_23).fover(d)
402
407
  # y = (d * B2.y - K * X12_23).fover(d)
403
- x, y = _pierlotxy2(B2, -K, Y12_23, X12_23, (X31_23 * Y12_23 -
404
- X12_23 * Y31_23))
408
+ x, y = _pierlotxy2(B2, -K, Y12_23, X12_23, Fdot_(X31_23, Y12_23,
409
+ -X12_23, Y31_23))
405
410
  else:
406
411
  x, y, _ = B2.xyz3
407
412
  return x, y, _zidw(x, y, useZ, B1, B2, B3)
@@ -492,11 +497,11 @@ def _pierlotx3(a_z_Bs, useZ, _cot, Cs):
492
497
  if K:
493
498
  cot23 = _cot(*sincos2d(a23))
494
499
 
495
- # x23 = x2_ + cot23 * y2_
496
- # y23 = y2_ - cot23 * x2_
500
+ # x23 = x2_ + cot23 * y2_ # _fma( cot23, y2_, x2_)
501
+ # y23 = y2_ - cot23 * x2_ # _fma(-cot23, x2_, y2_)
497
502
 
498
- # x31 = x1_ + cot23 * y1_
499
- # y31 = y1_ - cot23 * x1_
503
+ # x31 = x1_ + cot23 * y1_ # _fma( cot23, y1_, x1_)
504
+ # y31 = y1_ - cot23 * x1_ # _fma(-cot23, x1_, y1_)
500
505
 
501
506
  # x31 - x23 = x1_ + cot23 * y1_ - x2_ - cot23 * y2_
502
507
  X31_23 = _Fsumf_(x1_, cot23 * y1_, -x2_, -cot23 * y2_)
@@ -504,10 +509,10 @@ def _pierlotx3(a_z_Bs, useZ, _cot, Cs):
504
509
  Y31_23 = _Fsumf_(y1_, -cot23 * x1_, -y2_, cot23 * x2_)
505
510
 
506
511
  # d = (x31 - x23) * (x2_ - x1_) + (y31 - y23) * (y2_ - y1_)
507
- # x = (D * B3.x - K * Y31_23).fover(d)
508
- # y = (D * B3.y + K * X31_23).fover(d)
509
- x, y = _pierlotxy2(B3, K, Y31_23, X31_23, (X31_23 * _Fsumf_(x2_, -x1_) +
510
- Y31_23 * _Fsumf_(y2_, -y1_)))
512
+ # x = (d * B3.x - K * Y31_23).fover(d)
513
+ # y = (d * B3.y + K * X31_23).fover(d)
514
+ x, y = _pierlotxy2(B3, K, Y31_23, X31_23, Fdot_(X31_23, _Fsumf_(x2_, -x1_),
515
+ Y31_23, _Fsumf_(y2_, -y1_)))
511
516
  else:
512
517
  x, y, _ = B3.xyz3
513
518
  return x, y, _zidw(x, y, useZ, B1, B2, B3)
@@ -519,8 +524,8 @@ def _pierlotxy2(B, K, X, Y, D):
519
524
  d = float(D)
520
525
  if isnear0(d):
521
526
  raise ValueError(_or(_coincident_, _colinear_, _concyclic_))
522
- x = (D * B.x - K * X).fover(d)
523
- y = (D * B.y + K * Y).fover(d)
527
+ x = Fdot_(D, B.x, -K, X).fover(d)
528
+ y = Fdot_(D, B.y, K, Y).fover(d)
524
529
  return x, y
525
530
 
526
531
 
@@ -551,9 +556,7 @@ def snellius3(a, b, degC, alpha, beta):
551
556
  @see: Function L{wildberger3}.
552
557
  '''
553
558
  try:
554
- a, b, degC, alpha, beta = t = map1(float, a, b, degC, alpha, beta)
555
- if min(t) < 0:
556
- raise ValueError(_negative_)
559
+ a, b, degC, alpha, beta = _noneg(a, b, degC, alpha, beta)
557
560
  ra, rb, rC = map1(radians, alpha, beta, degC)
558
561
 
559
562
  r = fsum1f_(ra, rb, rC) * _0_5
@@ -705,17 +708,15 @@ def triAngle(a, b, c):
705
708
 
706
709
  def _triAngle(a, b, c):
707
710
  # (INTERNAL) To allow callers to embellish errors
708
- a, b, c = map1(float, a, b, c)
709
- if a < b:
711
+ a, b, c = _noneg(a, b, c)
712
+ if b > a:
710
713
  a, b = b, a
711
- if b < 0 or c < 0:
712
- raise ValueError(_negative_)
713
714
  if a < EPS0:
714
715
  raise ValueError(_coincident_)
715
716
  b_a = b / a
716
717
  if b_a < EPS0:
717
718
  raise ValueError(_coincident_)
718
- t = fsumf_(_1_0, b_a**2, -(c / a)**2) / (b_a * _2_0)
719
+ t = _Fsumf_(_1_0, b_a**2, -(c / a)**2).fover(b_a * _2_0)
719
720
  return acos1(t)
720
721
 
721
722
 
@@ -733,7 +734,7 @@ def triAngle5(a, b, c):
733
734
  C{radA}, C{radB} and C{radC} at triangle corners C{A}, C{B}
734
735
  and C{C}, all in C{radians}, the C{InCircle} radius C{rIn}
735
736
  aka C{inradius}, same units as triangle sides B{C{a}},
736
- B{C{b}} and B{C{c}} and the triangle C{area} in those same
737
+ B{C{b}} and B{C{c}} and the triangle C{area} in the same
737
738
  units I{squared}.
738
739
 
739
740
  @raise TriangleError: Invalid or negative B{C{a}}, B{C{b}} or B{C{c}}.
@@ -797,15 +798,15 @@ def triArea(a, b, c):
797
798
  try:
798
799
  r, y, x = sorted(map1(float, a, b, c))
799
800
  if r > 0: # r = min(a, b, c)
800
- ab = x - y
801
- bc = y - r
802
- y += r
803
- r = (x + y) * (r - ab) * (r + ab) * (x + bc)
801
+ z = r
802
+ d = x - y
803
+ r = (z + d) * (z - d)
804
804
  if r:
805
- r = sqrt(r / _16_0)
806
- elif r < 0:
805
+ x += y
806
+ r *= (x + z) * (x - z)
807
+ if r < 0:
807
808
  raise ValueError(_negative_)
808
- return r
809
+ return sqrt(r / _16_0) if r else _0_0
809
810
 
810
811
  except (TypeError, ValueError) as x:
811
812
  raise TriangleError(a=a, b=b, c=c, cause=x)
@@ -837,11 +838,8 @@ def triSide(a, b, radC):
837
838
 
838
839
  def _triSide(a, b, radC):
839
840
  # (INTERNAL) To allow callers to embellish errors
840
- a, b, r = t = map1(float, a, b, radC)
841
- if min(t) < 0:
842
- raise ValueError(_negative_)
843
-
844
- if a < b:
841
+ a, b, r = _noneg(a, b, radC)
842
+ if b < a:
845
843
  a, b = b, a
846
844
  if a > EPS0:
847
845
  ba = b / a
@@ -880,18 +878,21 @@ def triSide2(b, c, radB):
880
878
 
881
879
  def _triSide2(b, c, radB):
882
880
  # (INTERNAL) To allow callers to embellish errors
883
- b, c, rB = map1(float, b, c, radB)
884
- if min(b, c, rB) < 0:
885
- raise ValueError(_negative_)
881
+ b, c, rB = _noneg(b, c, radB)
886
882
  sB, cB = sincos2(rB)
887
- if isnear0(sB):
888
- if not isnear0(b):
883
+ if isnear0(b) or isnear0(sB):
884
+ if isnear0(b) and isnear0(sB):
885
+ if cB < 0:
886
+ rA = PI
887
+ a = b + c
888
+ else:
889
+ rA = _0_0
890
+ a = fabs(b - c)
891
+ else:
889
892
  raise ValueError(_invalid_)
890
- a, rA = ((b + c), PI) if cB < 0 else (fabs(b - c), _0_0)
891
- elif isnear0(b):
892
- raise ValueError(_invalid_)
893
893
  else:
894
- rA = fsumf_(PI, -rB, -asin1(c * sB / b))
894
+ rC = asin1(c * sB / b)
895
+ rA = max(fsumf_(PI, -rB, -rC), _0_0)
895
896
  a = sin(rA) * b / sB
896
897
  return TriSide2Tuple(a, rA, name=typename(triSide2))
897
898
 
@@ -918,9 +919,9 @@ def triSide4(radA, radB, c):
918
919
  and functions L{sqrt_a}, L{triSide} and L{triSide2}.
919
920
  '''
920
921
  try:
921
- rA, rB, c = map1(float, radA, radB, c)
922
+ rA, rB, c = _noneg(radA, radB, c)
922
923
  rC = fsumf_(PI, -rA, -rB)
923
- if min(rC, rA, rB, c) < 0:
924
+ if rC < 0:
924
925
  raise ValueError(_negative_)
925
926
  sa, ca, sb, cb = sincos2_(rA, rB)
926
927
  sc = fsum1f_(sa * cb, sb * ca)
@@ -965,18 +966,18 @@ def wildberger3(a, b, c, alpha, beta, R3=min):
965
966
  return sin(x)**2
966
967
 
967
968
  def _vpa(r3, q2, q3, s2, s3):
968
- r1 = s2 * q3 / s3
969
- r = r1 * r3 * _4_0
970
- n = (r - _Fsumf_(r1, r3, -q2)**2).fover(s3)
969
+ r1 = s2 * q3 / s3
970
+ r = r1 * r3 * _4_0
971
+ R = _Fsumf_(r1, r3, -q2)
972
+ R *= R # -(R**2 ...
973
+ R -= r # ... - r) / s3
974
+ n = -R.fover(s3)
971
975
  if n < 0 or r < EPS0:
972
976
  raise ValueError(_coincident_)
973
977
  return sqrt((n / r) * q3) if n else _0_0
974
978
 
975
979
  try:
976
- a, b, c, da, db = q = map1(float, a, b, c, alpha, beta)
977
- if min(q) < 0:
978
- raise ValueError(_negative_)
979
-
980
+ a, b, c, da, db = _noneg(a, b, c, alpha, beta)
980
981
  q1, q2, q3 = q = a**2, b**2, c**2
981
982
  if min(q) < EPS02:
982
983
  raise ValueError(_coincident_)
@@ -986,18 +987,19 @@ def wildberger3(a, b, c, alpha, beta, R3=min):
986
987
  if min(s) < EPS02:
987
988
  raise ValueError(_or(_coincident_, _colinear_))
988
989
 
989
- q4 = hypot2_(*q) * _2_0 # a**4 + ...
990
- Qs = _Fsumf_(*q) # == hypot2_(a, b, c)
991
- d0 = (Qs**2 - q4).fmul(s1 * s2).fover(s3)
992
- if d0 < 0:
993
- raise ValueError(_negative_)
990
+ Q = _Fsumf_(*q) # == a**2 + b**2 + ...
994
991
  s += _Fsumf_(*s), # == fsum1(s),
995
- C0 = Fdot(s, q1, q2, q3, -Qs * _0_5)
992
+ C0 = Fdot(s, q1, q2, q3, -Q * _0_5)
996
993
  r3 = C0.fover(-s3) # C0 /= -s3
994
+ Q *= Q # Q**2 - 2 * (a**4 + b**4 ...
995
+ Q -= hypot2_(*q) *_2_0 # ... + c**4)
996
+ d0 = Q.fmul(s1 * s2).fover(s3)
997
997
  if d0 > EPS02: # > c0
998
998
  _xcallable(R3=R3)
999
999
  d0 = sqrt(d0)
1000
1000
  r3 = R3(float(C0 + d0), float(C0 - d0)) # XXX min or max
1001
+ elif d0 < (-EPS02):
1002
+ raise ValueError(_negative_)
1001
1003
 
1002
1004
  pa = _vpa(r3, q2, q3, s2, s3)
1003
1005
  pb = _vpa(r3, q1, q3, s1, s3)
pygeodesy/utmupsBase.py CHANGED
@@ -7,8 +7,8 @@ for modules L{epsg}, L{etm}, L{mgrs}, L{ups} and L{utm}.
7
7
  # make sure int/int division yields float quotient, see .basics
8
8
  from __future__ import division as _; del _ # PYCHOK semicolon
9
9
 
10
- from pygeodesy.basics import _isin, isint, isscalar, isstr, neg_, \
11
- _xinstanceof, _xsubclassof
10
+ from pygeodesy.basics import _copysign, _isin, isint, isscalar, isstr, \
11
+ neg_, _xinstanceof, _xsubclassof
12
12
  from pygeodesy.constants import _float, _0_0, _0_5, _N_90_0, _180_0
13
13
  from pygeodesy.datums import _ellipsoidal_datum, _WGS84
14
14
  from pygeodesy.dms import degDMS, parseDMS2
@@ -29,10 +29,10 @@ from pygeodesy.streprs import Fmt, fstr, _fstrENH2, _xzipairs
29
29
  from pygeodesy.units import Band, Easting, Lat, Northing, Phi, Scalar, Zone
30
30
  from pygeodesy.utily import atan1, _Wrap, wrap360
31
31
 
32
- from math import cos, degrees, fabs, sin, tan
32
+ from math import cos, degrees, fabs, sin, tan # copysign as _copysign
33
33
 
34
34
  __all__ = _ALL_LAZY.utmupsBase
35
- __version__ = '25.04.25'
35
+ __version__ = '25.04.26'
36
36
 
37
37
  _UPS_BANDS = _A_, _B_, _Y_, _Z_ # UPS polar bands SE, SW, NE, NW
38
38
  # _UTM_BANDS = _MODS.utm._Bands
@@ -195,43 +195,65 @@ class UtmUpsBase(_NamedBase):
195
195
  self._notOverloaded(self)
196
196
 
197
197
  def _footpoint(self, y, lat0, makris):
198
- '''(INTERNAL) Compute to foot-point latitude in C{radians}.
198
+ '''(INTERNAL) Return the foot-point latitude in C{radians}.
199
199
  '''
200
+ F = _MODS.fsums.Fsum
201
+ E = self.datum.ellipsoid
200
202
  if y is None:
201
203
  _, y = self.eastingnorthing2(falsed=False)
202
- S = _MODS.fsums.Fsum
203
- E = self.datum.ellipsoid
204
- P = S(E.Llat(lat0), y)
204
+ B = F(E.Llat(lat0), y)
205
205
  if E.isSpherical:
206
- p = P.fover(E.a)
206
+ r = B.fover(E.a) # == E.b
207
+
207
208
  elif makris:
208
- r = P.fover(E.b)
209
- p = fabs(r)
210
- if p:
209
+ b = B.fover(E.b)
210
+ r = fabs(b)
211
+ if r:
211
212
  e2 = E.e22 # E.e22abs?
212
213
  e4 = E.e4
213
214
 
214
- e1 = S(e2 / 4, -11 / 64 * e4, -1).as_iscalar
215
- e2 = S(e2 / 8, -13 / 128 * e4) .as_iscalar
216
- e4 *= cos(p)**2 / 8
217
-
218
- s = sin(p * 2)
219
- p = -p
220
- U = S(e1 * p, e2 * s, e4 * p, (5 / 8 * e4) * s**2)
221
- p = atan1(E.a * tan(float(U)) / E.b)
222
- if r < 0: # copysign(p, y)
223
- p = -p
224
- else: # PyGeodetics
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
225
245
  f = E.f
226
246
  f2 = f**2
227
247
  f3 = f**3
228
- B0 = S(1, -f / 2, f2 / 16, f3 / 32) * E.a
229
- r = P.fover(B0)
230
- P = S(r, S(3 / 4 * f, 3 / 8 * f2, 21 / 256 * f3) * sin(r * 2),
231
- S( 21 / 64 * f2, 21 / 64 * f3) * sin(r * 4),
232
- 151 / 768 * f3 * sin(r * 6))
233
- p = float(P)
234
- return p
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
235
257
 
236
258
  @Property_RO
237
259
  def gamma(self):
@@ -251,8 +273,8 @@ class UtmUpsBase(_NamedBase):
251
273
  def latFootPoint(self, northing=None, lat0=0, makris=False):
252
274
  '''Compute the foot-point latitude in C{degrees}.
253
275
 
254
- @arg northing: Northing (C{meter}, same units this datum's ellipsoid's axes),
255
- overriding this I{unfalsed} northing.
276
+ @arg northing: Northing (C{meter}, same units this ellipsoid's axes),
277
+ overriding this northing, I{unfalsed}.
256
278
  @kwarg lat0: Geodetic latitude of the meridian's origin (C{degrees}).
257
279
  @kwarg makris: If C{True}, use C{Makris}' formula, otherwise C{PyGeodetics}'.
258
280
 
pygeodesy/vector2d.py CHANGED
@@ -16,7 +16,7 @@ from pygeodesy.constants import EPS, EPS0, EPS02, EPS4, INF, INT0, \
16
16
  _1_0, _1_0_1T, _N_1_0, _2_0, _N_2_0, _4_0
17
17
  from pygeodesy.errors import _and, _AssertionError, IntersectionError, NumPyError, \
18
18
  PointsError, TriangleError, _xError, _xkwds
19
- from pygeodesy.fmath import fabs, fdot, fdot_, hypot, hypot2_, sqrt
19
+ from pygeodesy.fmath import fabs, fdot, Fdot_, fdot_, hypot, hypot2_, sqrt
20
20
  from pygeodesy.fsums import _Fsumf_, fsumf_, fsum1f_
21
21
  # from pygeodesy.internals import typename # from .basics
22
22
  from pygeodesy.interns import NN, _a_, _and_, _b_, _c_, _center_, _coincident_, \
@@ -36,7 +36,7 @@ from contextlib import contextmanager
36
36
  # from math import fabs, sqrt # from .fmath
37
37
 
38
38
  __all__ = _ALL_LAZY.vector2d
39
- __version__ = '25.04.16'
39
+ __version__ = '25.04.30'
40
40
 
41
41
  _cA_ = 'cA'
42
42
  _cB_ = 'cB'
@@ -755,8 +755,8 @@ def _trilaterate2d2(x1, y1, radius1, x2, y2, radius2, x3, y3, radius3,
755
755
  raise IntersectionError(_and(_astr(x1=x1, y1=y1, radius1=r1),
756
756
  _astr(x2=x2, y2=y2, radius2=r2),
757
757
  _astr(x3=x3, y3=y3, radius3=r3)), txt=t)
758
- t = Vector2Tuple(fdot_(c, e, -b, f) / q,
759
- fdot_(a, f, -c, d) / q, name=typename(trilaterate2d2))
758
+ t = Vector2Tuple(Fdot_(c, e, -b, f).fover(q),
759
+ Fdot_(a, f, -c, d).fover(q), name=typename(trilaterate2d2))
760
760
 
761
761
  if eps and eps > 0: # check distances to center vs radius
762
762
  for x, y, r in ((x1, y1, r1), (x2, y2, r2), (x3, y3, r3)):