pygeodesy 24.11.11__py2.py3-none-any.whl → 25.1.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 (118) hide show
  1. {PyGeodesy-24.11.11.dist-info → PyGeodesy-25.1.5.dist-info}/METADATA +34 -35
  2. PyGeodesy-25.1.5.dist-info/RECORD +118 -0
  3. {PyGeodesy-24.11.11.dist-info → PyGeodesy-25.1.5.dist-info}/WHEEL +1 -1
  4. pygeodesy/__init__.py +19 -19
  5. pygeodesy/__main__.py +1 -1
  6. pygeodesy/albers.py +5 -5
  7. pygeodesy/auxilats/_CX_4.py +1 -1
  8. pygeodesy/auxilats/_CX_6.py +1 -1
  9. pygeodesy/auxilats/_CX_8.py +1 -1
  10. pygeodesy/auxilats/_CX_Rs.py +1 -1
  11. pygeodesy/auxilats/__init__.py +1 -1
  12. pygeodesy/auxilats/__main__.py +1 -1
  13. pygeodesy/auxilats/auxAngle.py +5 -5
  14. pygeodesy/auxilats/auxDLat.py +6 -6
  15. pygeodesy/auxilats/auxDST.py +2 -2
  16. pygeodesy/auxilats/auxLat.py +5 -5
  17. pygeodesy/auxilats/auxily.py +2 -2
  18. pygeodesy/azimuthal.py +5 -5
  19. pygeodesy/basics.py +60 -8
  20. pygeodesy/booleans.py +1 -1
  21. pygeodesy/cartesianBase.py +22 -61
  22. pygeodesy/clipy.py +1 -1
  23. pygeodesy/constants.py +5 -5
  24. pygeodesy/css.py +1 -1
  25. pygeodesy/datums.py +1 -1
  26. pygeodesy/deprecated/__init__.py +2 -2
  27. pygeodesy/deprecated/bases.py +1 -1
  28. pygeodesy/deprecated/classes.py +86 -2
  29. pygeodesy/deprecated/consterns.py +1 -1
  30. pygeodesy/deprecated/datum.py +5 -5
  31. pygeodesy/deprecated/functions.py +42 -8
  32. pygeodesy/deprecated/nvector.py +1 -1
  33. pygeodesy/deprecated/rhumbBase.py +1 -1
  34. pygeodesy/deprecated/rhumbaux.py +1 -1
  35. pygeodesy/deprecated/rhumbsolve.py +1 -1
  36. pygeodesy/deprecated/rhumbx.py +1 -1
  37. pygeodesy/dms.py +1 -1
  38. pygeodesy/ecef.py +53 -56
  39. pygeodesy/elevations.py +1 -1
  40. pygeodesy/ellipsoidalBase.py +3 -3
  41. pygeodesy/ellipsoidalBaseDI.py +1 -1
  42. pygeodesy/ellipsoidalExact.py +1 -1
  43. pygeodesy/ellipsoidalGeodSolve.py +1 -1
  44. pygeodesy/ellipsoidalKarney.py +1 -1
  45. pygeodesy/ellipsoidalNvector.py +1 -1
  46. pygeodesy/ellipsoidalVincenty.py +6 -5
  47. pygeodesy/ellipsoids.py +4 -5
  48. pygeodesy/elliptic.py +6 -6
  49. pygeodesy/epsg.py +1 -1
  50. pygeodesy/errors.py +1 -1
  51. pygeodesy/etm.py +5 -5
  52. pygeodesy/fmath.py +18 -17
  53. pygeodesy/formy.py +409 -555
  54. pygeodesy/frechet.py +29 -86
  55. pygeodesy/fstats.py +1 -1
  56. pygeodesy/fsums.py +32 -33
  57. pygeodesy/gars.py +1 -1
  58. pygeodesy/geodesici.py +7 -7
  59. pygeodesy/geodesicw.py +1 -1
  60. pygeodesy/geodesicx/_C4_24.py +2 -2
  61. pygeodesy/geodesicx/_C4_27.py +2 -2
  62. pygeodesy/geodesicx/_C4_30.py +2 -2
  63. pygeodesy/geodesicx/__init__.py +2 -2
  64. pygeodesy/geodesicx/__main__.py +4 -5
  65. pygeodesy/geodesicx/gx.py +6 -5
  66. pygeodesy/geodesicx/gxarea.py +2 -2
  67. pygeodesy/geodesicx/gxbases.py +2 -2
  68. pygeodesy/geodesicx/gxline.py +16 -12
  69. pygeodesy/geodsolve.py +1 -1
  70. pygeodesy/geohash.py +1 -1
  71. pygeodesy/geoids.py +277 -203
  72. pygeodesy/hausdorff.py +23 -81
  73. pygeodesy/heights.py +115 -150
  74. pygeodesy/internals.py +1 -1
  75. pygeodesy/interns.py +2 -3
  76. pygeodesy/iters.py +1 -1
  77. pygeodesy/karney.py +3 -3
  78. pygeodesy/ktm.py +16 -15
  79. pygeodesy/latlonBase.py +323 -409
  80. pygeodesy/lazily.py +53 -44
  81. pygeodesy/lcc.py +1 -1
  82. pygeodesy/ltp.py +46 -50
  83. pygeodesy/ltpTuples.py +147 -130
  84. pygeodesy/mgrs.py +1 -1
  85. pygeodesy/named.py +149 -3
  86. pygeodesy/namedTuples.py +58 -7
  87. pygeodesy/nvectorBase.py +122 -105
  88. pygeodesy/osgr.py +1 -1
  89. pygeodesy/points.py +1 -1
  90. pygeodesy/props.py +1 -1
  91. pygeodesy/resections.py +18 -17
  92. pygeodesy/rhumb/__init__.py +1 -1
  93. pygeodesy/rhumb/aux_.py +2 -2
  94. pygeodesy/rhumb/bases.py +2 -2
  95. pygeodesy/rhumb/ekx.py +4 -4
  96. pygeodesy/rhumb/solve.py +1 -1
  97. pygeodesy/simplify.py +289 -401
  98. pygeodesy/solveBase.py +1 -1
  99. pygeodesy/sphericalBase.py +1 -1
  100. pygeodesy/sphericalNvector.py +5 -5
  101. pygeodesy/sphericalTrigonometry.py +7 -6
  102. pygeodesy/streprs.py +10 -5
  103. pygeodesy/trf.py +1 -1
  104. pygeodesy/triaxials.py +23 -16
  105. pygeodesy/units.py +16 -16
  106. pygeodesy/unitsBase.py +1 -1
  107. pygeodesy/ups.py +4 -4
  108. pygeodesy/utily.py +341 -211
  109. pygeodesy/utm.py +5 -5
  110. pygeodesy/utmups.py +1 -1
  111. pygeodesy/utmupsBase.py +1 -1
  112. pygeodesy/vector2d.py +5 -5
  113. pygeodesy/vector3d.py +14 -3
  114. pygeodesy/vector3dBase.py +5 -5
  115. pygeodesy/webmercator.py +1 -1
  116. pygeodesy/wgrs.py +1 -1
  117. PyGeodesy-24.11.11.dist-info/RECORD +0 -118
  118. {PyGeodesy-24.11.11.dist-info → PyGeodesy-25.1.5.dist-info}/top_level.txt +0 -0
pygeodesy/formy.py CHANGED
@@ -6,19 +6,19 @@ u'''Formulary of basic geodesy functions and approximations.
6
6
  # make sure int/int division yields float quotient, see .basics
7
7
  from __future__ import division as _; del _ # PYCHOK semicolon
8
8
 
9
- # from pygeodesy.basics import W_args_kwds_count2
9
+ # from pygeodesy.basics import _args_kwds_count2, _copysign # from .constants
10
10
  # from pygeodesy.cartesianBase import CartesianBase # _MODS
11
11
  from pygeodesy.constants import EPS, EPS0, EPS1, PI, PI2, PI3, PI_2, R_M, \
12
12
  _0_0s, float0_, isnon0, remainder, _umod_PI2, \
13
13
  _0_0, _0_125, _0_25, _0_5, _1_0, _2_0, _4_0, \
14
- _32_0, _90_0, _180_0, _360_0
14
+ _90_0, _180_0, _360_0, _copysign
15
15
  from pygeodesy.datums import Datum, Ellipsoid, _ellipsoidal_datum, \
16
16
  _mean_radius, _spherical_datum, _WGS84, _EWGS84
17
17
  # from pygeodesy.ellipsoids import Ellipsoid, _EWGS84 # from .datums
18
18
  from pygeodesy.errors import IntersectionError, LimitError, limiterrors, \
19
19
  _TypeError, _ValueError, _xattr, _xError, \
20
20
  _xcallable,_xkwds, _xkwds_pop2
21
- from pygeodesy.fmath import euclid, hypot, hypot2, sqrt0
21
+ from pygeodesy.fmath import euclid, fdot_, fprod, hypot, hypot2, sqrt0
22
22
  from pygeodesy.fsums import fsumf_, Fmt, unstr
23
23
  # from pygeodesy.internals import _DUNDER_nameof # from .named
24
24
  from pygeodesy.interns import _delta_, _distant_, _inside_, _SPACE_, _too_
@@ -26,30 +26,47 @@ from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS
26
26
  from pygeodesy.named import _name__, _name2__, _NamedTuple, _xnamed, \
27
27
  _DUNDER_nameof
28
28
  from pygeodesy.namedTuples import Bearing2Tuple, Distance4Tuple, LatLon2Tuple, \
29
- Intersection3Tuple, PhiLam2Tuple, Vector3Tuple
29
+ Intersection3Tuple, PhiLam2Tuple
30
30
  # from pygeodesy.streprs import Fmt, unstr # from .fsums
31
31
  # from pygeodesy.triaxials import _hartzell3 # _MODS
32
- from pygeodesy.units import _isHeight, _isRadius, Bearing, Degrees_, Distance, \
33
- Distance_, Height, Lamd, Lat, Lon, Meter_, Phid, \
34
- Radians, Radians_, Radius, Radius_, Scalar, _100km
35
- from pygeodesy.utily import acos1, atan2b, atan2d, degrees2m, _loneg, m2degrees, \
36
- tan_2, sincos2, sincos2_, sincos2d_, _Wrap
32
+ from pygeodesy.units import _isDegrees, _isHeight, _isRadius, Bearing, Degrees_, \
33
+ Distance, Distance_, Height, Lamd, Lat, Lon, Meter_, \
34
+ Phid, Radians, Radians_, Radius, Radius_, Scalar, _100km
35
+ from pygeodesy.utily import acos1, asin1, atan2, atan2b, degrees2m, hav, _loneg, \
36
+ m2degrees, tan_2, sincos2, sincos2_, _Wrap
37
37
  # from pygeodesy.vector3d import _otherV3d # _MODS
38
38
  # from pygeodesy.vector3dBase import _xyz_y_z3 # _MODS
39
39
  # from pygeodesy import ellipsoidalExact, ellipsoidalKarney, vector3d, \
40
40
  # sphericalNvector, sphericalTrigonometry # _MODS
41
41
 
42
42
  from contextlib import contextmanager
43
- from math import asin, atan, atan2, cos, degrees, fabs, radians, sin, sqrt # pow
43
+ from math import atan, cos, degrees, fabs, radians, sin, sqrt # pow
44
44
 
45
45
  __all__ = _ALL_LAZY.formy
46
- __version__ = '24.10.14'
46
+ __version__ = '25.01.05'
47
47
 
48
- _RADIANS2 = (PI / _180_0)**2 # degrees- to radians-squared
48
+ _RADIANS2 = radians(_1_0)**2 # degree to radians-squared
49
49
  _ratio_ = 'ratio'
50
50
  _xline_ = 'xline'
51
51
 
52
52
 
53
+ def angle2chord(rad, radius=R_M):
54
+ '''Get the chord length of a (central) angle or I{angular} distance.
55
+
56
+ @arg rad: Central angle (C{radians}).
57
+ @kwarg radius: Mean earth radius (C{meter}, conventionally), datum (L{Datum}) or ellipsoid
58
+ (L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}) to use or C{None}.
59
+
60
+ @return: Chord length (C{meter}, same units as B{C{radius}} or if C{B{radius} is None}, C{radians}).
61
+
62
+ @see: Function L{chord2angle}, method L{intermediateChordTo<sphericalNvector.LatLon.intermediateChordTo>} and
63
+ U{great-circle-distance<https://WikiPedia.org/wiki/Great-circle_distance#Relation_between_central_angle_and_chord_length>}.
64
+ '''
65
+ d = _isDegrees(rad, iscalar=False)
66
+ r = sin((radians(rad) if d else rad) / _2_0) * _2_0
67
+ return (degrees(r) if d else r) if radius is None else (_mean_radius(radius) * r)
68
+
69
+
53
70
  def _anti2(a, b, n_2, n, n2):
54
71
  '''(INTERNAL) Helper for C{antipode} and C{antipode_}.
55
72
  '''
@@ -95,18 +112,17 @@ def antipode_(phi, lam, **name):
95
112
 
96
113
 
97
114
  def bearing(lat1, lon1, lat2, lon2, **final_wrap):
98
- '''Compute the initial or final bearing (forward or reverse azimuth)
99
- between two (spherical) points.
115
+ '''Compute the initial or final bearing (forward or reverse azimuth) between two
116
+ (spherical) points.
100
117
 
101
118
  @arg lat1: Start latitude (C{degrees}).
102
119
  @arg lon1: Start longitude (C{degrees}).
103
120
  @arg lat2: End latitude (C{degrees}).
104
121
  @arg lon2: End longitude (C{degrees}).
105
- @kwarg final_wrap: Optional keyword arguments for function
106
- L{pygeodesy.bearing_}.
122
+ @kwarg final_wrap: Optional keyword arguments for function L{pygeodesy.bearing_}.
107
123
 
108
- @return: Initial or final bearing (compass C{degrees360}) or zero if
109
- both points coincide.
124
+ @return: Initial or final bearing (compass C{degrees360}) or zero if both points
125
+ coincide.
110
126
  '''
111
127
  r = bearing_(Phid(lat1=lat1), Lamd(lon1=lon1),
112
128
  Phid(lat2=lat2), Lamd(lon2=lon2), **final_wrap)
@@ -114,20 +130,19 @@ def bearing(lat1, lon1, lat2, lon2, **final_wrap):
114
130
 
115
131
 
116
132
  def bearing_(phi1, lam1, phi2, lam2, final=False, wrap=False):
117
- '''Compute the initial or final bearing (forward or reverse azimuth) between
118
- two (spherical) points.
133
+ '''Compute the initial or final bearing (forward or reverse azimuth) between two
134
+ (spherical) points.
119
135
 
120
136
  @arg phi1: Start latitude (C{radians}).
121
137
  @arg lam1: Start longitude (C{radians}).
122
138
  @arg phi2: End latitude (C{radians}).
123
139
  @arg lam2: End longitude (C{radians}).
124
- @kwarg final: If C{True}, return the final, otherwise the initial bearing
125
- (C{bool}).
126
- @kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{phi2}} and
127
- B{C{lam2}} (C{bool}).
140
+ @kwarg final: If C{True}, return the final, otherwise the initial bearing (C{bool}).
141
+ @kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{phi2}} and B{C{lam2}}
142
+ (C{bool}).
128
143
 
129
- @return: Initial or final bearing (compass C{radiansPI2}) or zero if both
130
- are coincident.
144
+ @return: Initial or final bearing (compass C{radiansPI2}) or zero if both points
145
+ coincide.
131
146
 
132
147
  @see: U{Bearing<https://www.Movable-Type.co.UK/scripts/latlong.html>}, U{Course
133
148
  between two points<https://www.EdWilliams.org/avform147.htm#Crs>} and
@@ -156,11 +171,36 @@ def _bearingTo2(p1, p2, wrap=False): # for points.ispolar, sphericalTrigonometr
156
171
  pass
157
172
  # XXX spherical version, OK for ellipsoidal ispolar?
158
173
  t = p1.philam + p2.philam
159
- return Bearing2Tuple(degrees(bearing_(*t, final=False, wrap=wrap)),
160
- degrees(bearing_(*t, final=True, wrap=wrap)),
174
+ i = bearing_(*t, final=False, wrap=wrap)
175
+ f = bearing_(*t, final=True, wrap=wrap)
176
+ return Bearing2Tuple(degrees(i), degrees(f),
161
177
  name__=_bearingTo2)
162
178
 
163
179
 
180
+ def chord2angle(chord, radius=R_M):
181
+ '''Get the (central) angle from a chord length or distance.
182
+
183
+ @arg chord: Length or distance (C{meter}, same units as B{C{radius}}).
184
+ @kwarg radius: Mean earth radius (C{meter}, conventionally), datum (L{Datum}) or
185
+ ellipsoid (L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}) to use.
186
+
187
+ @return: Angle (C{radians} with sign of B{C{chord}}) or C{0} if C{B{radius}=0}.
188
+
189
+ @note: The angle will exceed C{PI} if C{B{chord} > B{radius} * 2}.
190
+
191
+ @see: Function L{angle2chord}.
192
+ '''
193
+ m = _mean_radius(radius)
194
+ r = fabs(chord / (m * _2_0)) if m > 0 else _0_0
195
+ if r:
196
+ i = int(r)
197
+ if i > 0:
198
+ r -= i
199
+ i *= PI
200
+ r = (asin1(r) + i) * _2_0
201
+ return _copysign(r, chord)
202
+
203
+
164
204
  def compassAngle(lat1, lon1, lat2, lon2, adjust=True, wrap=False):
165
205
  '''Return the angle from North for the direction vector M{(lon2 - lon1,
166
206
  lat2 - lat1)} between two points.
@@ -172,10 +212,10 @@ def compassAngle(lat1, lon1, lat2, lon2, adjust=True, wrap=False):
172
212
  @arg lon1: From longitude (C{degrees}).
173
213
  @arg lat2: To latitude (C{degrees}).
174
214
  @arg lon2: To longitude (C{degrees}).
175
- @kwarg adjust: Adjust the longitudinal delta by the cosine of the
176
- mean latitude (C{bool}).
177
- @kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{lat2}}
178
- and B{C{lon2}} (C{bool}).
215
+ @kwarg adjust: Adjust the longitudinal delta by the cosine of the mean
216
+ latitude (C{bool}).
217
+ @kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{lat2}} and
218
+ B{C{lon2}} (C{bool}).
179
219
 
180
220
  @return: Compass angle from North (C{degrees360}).
181
221
 
@@ -190,230 +230,144 @@ def compassAngle(lat1, lon1, lat2, lon2, adjust=True, wrap=False):
190
230
  return atan2b(d_lon, lat2 - lat1)
191
231
 
192
232
 
193
- def cosineAndoyerLambert(lat1, lon1, lat2, lon2, datum=_WGS84, wrap=False):
194
- '''Compute the distance between two (ellipsoidal) points using the U{Andoyer-Lambert
195
- <https://books.google.com/books?id=x2UiAQAAIAAJ>} correction of the U{Law of
196
- Cosines<https://www.Movable-Type.co.UK/scripts/latlong.html#cosine-law>} formula.
233
+ def cosineLaw(lat1, lon1, lat2, lon2, corr=0, earth=None, wrap=False,
234
+ datum=_WGS84, radius=R_M):
235
+ '''Compute the distance between two points using the U{Law of Cosines
236
+ <https://www.Movable-Type.co.UK/scripts/latlong.html#cosine-law>}
237
+ formula, optionally corrected.
197
238
 
198
239
  @arg lat1: Start latitude (C{degrees}).
199
240
  @arg lon1: Start longitude (C{degrees}).
200
241
  @arg lat2: End latitude (C{degrees}).
201
242
  @arg lon2: End longitude (C{degrees}).
202
- @kwarg datum: Datum (L{Datum}) or ellipsoid (L{Ellipsoid}, L{Ellipsoid2} or
203
- L{a_f2Tuple}) to use.
204
- @kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{lat2}} and
205
- B{C{lon2}} (C{bool}).
243
+ @kwarg corr: Use C{B{corr}=2} to apply the U{Forsythe-Andoyer-Lambert
244
+ <https://www2.UNB.CA/gge/Pubs/TR77.pdf>}, C{B{corr}=1} for the
245
+ U{Andoyer-Lambert<https://Books.Google.com/books?id=x2UiAQAAIAAJ>}
246
+ corrected (ellipsoidal) or keep C{B{corr}=0} for the uncorrected
247
+ (spherical) C{Law of Cosines} formula (C{int}).
248
+ @kwarg earth: Mean earth radius (C{meter}) or datum (L{Datum}) or ellipsoid
249
+ (L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}) to use.
250
+ @kwarg wrap: If C{True}, wrap or I{normalize} and B{C{lat2}} and B{C{lon2}}
251
+ (C{bool}).
252
+ @kwarg datum: Default ellipsiodal B{C{earth}} (and for backward compatibility).
253
+ @kwarg radius: Default spherical B{C{earth}} (and for backward compatibility).
206
254
 
207
- @return: Distance (C{meter}, same units as the B{C{datum}}'s ellipsoid axes or
208
- C{radians} if C{B{datum} is None}).
255
+ @return: Distance (C{meter}, same units as B{C{radius}} or the datum's or
256
+ ellipsoid axes).
209
257
 
210
- @raise TypeError: Invalid B{C{datum}}.
258
+ @raise TypeError: Invalid B{C{earth}}, B{C{datum}} or B{C{radius}}.
211
259
 
212
- @see: Functions L{cosineAndoyerLambert_}, L{cosineForsytheAndoyerLambert},
213
- L{cosineLaw}, L{equirectangular}, L{euclidean}, L{flatLocal}/L{hubeny},
214
- L{flatPolar}, L{haversine}, L{thomas} and L{vincentys} and method
215
- L{Ellipsoid.distance2}.
260
+ @raise ValueError: Invalid B{C{corr}}.
261
+
262
+ @see: Functions L{cosineLaw_}, L{equirectangular}, L{euclidean}, L{flatLocal} /
263
+ L{hubeny}, L{flatPolar}, L{haversine}, L{thomas} and L{vincentys} and
264
+ method L{Ellipsoid.distance2}.
265
+
266
+ @note: See note at function L{vincentys_}.
216
267
  '''
217
- return _dE(cosineAndoyerLambert_, datum, wrap, lat1, lon1, lat2, lon2)
268
+ return _dE(cosineLaw_, earth or datum, wrap, lat1, lon1, lat2, lon2, corr=corr) if corr else \
269
+ _dS(cosineLaw_, earth or radius, wrap, lat1, lon1, lat2, lon2)
218
270
 
219
271
 
220
- def cosineAndoyerLambert_(phi2, phi1, lam21, datum=_WGS84):
221
- '''Compute the I{angular} distance between two (ellipsoidal) points using the U{Andoyer-Lambert
222
- <https://books.google.com/books?id=x2UiAQAAIAAJ>} correction of the U{Law of
223
- Cosines<https://www.Movable-Type.co.UK/scripts/latlong.html#cosine-law>} formula.
272
+ def cosineLaw_(phi2, phi1, lam21, corr=0, earth=None, datum=_WGS84):
273
+ '''Compute the I{angular} distance between two points using the U{Law of Cosines
274
+ <https://www.Movable-Type.co.UK/scripts/latlong.html#cosine-law>} formula,
275
+ optionally corrected.
224
276
 
225
277
  @arg phi2: End latitude (C{radians}).
226
278
  @arg phi1: Start latitude (C{radians}).
227
279
  @arg lam21: Longitudinal delta, M{end-start} (C{radians}).
228
- @kwarg datum: Datum (L{Datum}) or ellipsoid (L{Ellipsoid}, L{Ellipsoid2} or
229
- L{a_f2Tuple}) to use.
280
+ @kwarg corr: Use C{B{corr}=2} to apply the U{Forsythe-Andoyer-Lambert
281
+ <https://www2.UNB.CA/gge/Pubs/TR77.pdf>}, C{B{corr}=1} for the
282
+ U{Andoyer-Lambert<https://Books.Google.com/books?id=x2UiAQAAIAAJ>}
283
+ corrected (ellipsoidal) or keep C{B{corr}=0} for the uncorrected
284
+ (spherical) C{Law of Cosines} formula (C{int}).
285
+ @kwarg earth: Mean earth radius (C{meter}) or datum (L{Datum}) or ellipsoid
286
+ (L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}) to use.
287
+ @kwarg datum: Default ellipsoidal B{C{earth}} (and for backward compatibility).
230
288
 
231
289
  @return: Angular distance (C{radians}).
232
290
 
233
- @raise TypeError: Invalid B{C{datum}}.
291
+ @raise TypeError: Invalid B{C{earth}} or B{C{datum}}.
292
+
293
+ @raise ValueError: Invalid B{C{corr}}.
234
294
 
235
- @see: Functions L{cosineAndoyerLambert}, L{cosineForsytheAndoyerLambert_},
236
- L{cosineLaw_}, L{euclidean_}, L{flatLocal_}/L{hubeny_}, L{flatPolar_},
237
- L{haversine_}, L{thomas_} and L{vincentys_} and U{Geodesy-PHP
295
+ @see: Functions L{cosineLaw}, L{euclidean_}, L{flatLocal_} / L{hubeny_},
296
+ L{flatPolar_}, L{haversine_}, L{thomas_} and L{vincentys_} and U{Geodesy-PHP
238
297
  <https://GitHub.com/jtejido/geodesy-php/blob/master/src/Geodesy/Distance/
239
298
  AndoyerLambert.php>}.
240
299
  '''
241
300
  s2, c2, s1, c1, r, c21 = _sincosa6(phi2, phi1, lam21)
242
- if isnon0(c1) and isnon0(c2):
243
- E = _ellipsoidal(datum, cosineAndoyerLambert_)
244
- if E.f: # ellipsoidal
245
- r2 = atan2(E.b_a * s2, c2)
246
- r1 = atan2(E.b_a * s1, c1)
247
- s2, c2, s1, c1 = sincos2_(r2, r1)
248
- r = acos1(s1 * s2 + c1 * c2 * c21)
249
- if r:
250
- sr, _, sr_2, cr_2 = sincos2_(r, r * _0_5)
251
- if isnon0(sr_2) and isnon0(cr_2):
252
- s = (sr + r) * ((s1 - s2) / sr_2)**2
253
- c = (sr - r) * ((s1 + s2) / cr_2)**2
254
- r += (c - s) * E.f * _0_125
255
- return r
256
-
257
-
258
- def cosineForsytheAndoyerLambert(lat1, lon1, lat2, lon2, datum=_WGS84, wrap=False):
259
- '''Compute the distance between two (ellipsoidal) points using the U{Forsythe-Andoyer-Lambert
260
- <https://www2.UNB.Ca/gge/Pubs/TR77.pdf>} correction of the U{Law of Cosines
261
- <https://www.Movable-Type.co.UK/scripts/latlong.html#cosine-law>} formula.
262
-
263
- @arg lat1: Start latitude (C{degrees}).
264
- @arg lon1: Start longitude (C{degrees}).
265
- @arg lat2: End latitude (C{degrees}).
266
- @arg lon2: End longitude (C{degrees}).
267
- @kwarg datum: Datum (L{Datum}) or ellipsoid (L{Ellipsoid}, L{Ellipsoid2} or
268
- L{a_f2Tuple}) to use.
269
- @kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{lat2}} and
270
- B{C{lon2}} (C{bool}).
271
-
272
- @return: Distance (C{meter}, same units as the B{C{datum}}'s ellipsoid axes or
273
- C{radians} if C{B{datum} is None}).
274
-
275
- @raise TypeError: Invalid B{C{datum}}.
276
-
277
- @see: Functions L{cosineForsytheAndoyerLambert_}, L{cosineAndoyerLambert},
278
- L{cosineLaw}, L{equirectangular}, L{euclidean}, L{flatLocal}/L{hubeny},
279
- L{flatPolar}, L{haversine}, L{thomas} and L{vincentys} and method
280
- L{Ellipsoid.distance2}.
281
- '''
282
- return _dE(cosineForsytheAndoyerLambert_, datum, wrap, lat1, lon1, lat2, lon2)
283
-
284
-
285
- def cosineForsytheAndoyerLambert_(phi2, phi1, lam21, datum=_WGS84):
286
- '''Compute the I{angular} distance between two (ellipsoidal) points using the
287
- U{Forsythe-Andoyer-Lambert<https://www2.UNB.Ca/gge/Pubs/TR77.pdf>} correction of
288
- the U{Law of Cosines<https://www.Movable-Type.co.UK/scripts/latlong.html#cosine-law>}
289
- formula.
290
-
291
- @arg phi2: End latitude (C{radians}).
292
- @arg phi1: Start latitude (C{radians}).
293
- @arg lam21: Longitudinal delta, M{end-start} (C{radians}).
294
- @kwarg datum: Datum (L{Datum}) or ellipsoid to use (L{Ellipsoid},
295
- L{Ellipsoid2} or L{a_f2Tuple}).
296
-
297
- @return: Angular distance (C{radians}).
298
-
299
- @raise TypeError: Invalid B{C{datum}}.
300
-
301
- @see: Functions L{cosineForsytheAndoyerLambert}, L{cosineAndoyerLambert_},
302
- L{cosineLaw_}, L{euclidean_}, L{flatLocal_}/L{hubeny_}, L{flatPolar_},
303
- L{haversine_}, L{thomas_} and L{vincentys_} and U{Geodesy-PHP
304
- <https://GitHub.com/jtejido/geodesy-php/blob/master/src/Geodesy/
305
- Distance/ForsytheCorrection.php>}.
306
- '''
307
- s2, c2, s1, c1, r, _ = _sincosa6(phi2, phi1, lam21)
308
- if r and isnon0(c1) and isnon0(c2):
309
- E = _ellipsoidal(datum, cosineForsytheAndoyerLambert_)
310
- if E.f: # ellipsoidal
311
- sr, cr, s2r, _ = sincos2_(r, r * 2)
312
- if isnon0(sr) and fabs(cr) < EPS1:
313
- s = (s1 + s2)**2 / (1 + cr)
314
- t = (s1 - s2)**2 / (1 - cr)
315
- x = s + t
316
- y = s - t
317
-
318
- s = 8 * r**2 / sr
319
- a = 64 * r + s * cr * 2 # 16 * r**2 / tan(r)
320
- d = 48 * sr + s # 8 * r**2 / tan(r)
321
- b = -2 * d
322
- e = 30 * s2r
323
- c = fsumf_(30 * r, e * _0_5, s * cr) # 8 * r**2 / tan(r)
324
- t = fsumf_( a * x, e * y**2, b * y, -c * x**2, d * x * y)
325
-
326
- r += fsumf_(-r * x, 3 * y * sr, t * E.f / _32_0) * E.f * _0_25
301
+ if corr and isnon0(c1) and isnon0(c2):
302
+ E = _ellipsoidal(earth or datum, cosineLaw_)
303
+ f = _0_25 * E.f
304
+ if f: # ellipsoidal
305
+ if corr == 1: # Andoyer-Lambert
306
+ r2 = atan2(E.b_a * s2, c2)
307
+ r1 = atan2(E.b_a * s1, c1)
308
+ s2, c2, s1, c1 = sincos2_(r2, r1)
309
+ r = acos1(s1 * s2 + c1 * c2 * c21)
310
+ if r:
311
+ sr, _, sr_2, cr_2 = sincos2_(r, r * _0_5)
312
+ if isnon0(sr_2) and isnon0(cr_2):
313
+ s = (sr + r) * ((s1 - s2) / sr_2)**2
314
+ c = (sr - r) * ((s1 + s2) / cr_2)**2
315
+ r += (c - s) * _0_5 * f
316
+
317
+ elif corr == 2: # Forsythe-Andoyer-Lambert
318
+ sr, cr, s2r, _ = sincos2_(r, r * 2)
319
+ if isnon0(sr) and fabs(cr) < EPS1:
320
+ s = (s1 + s2)**2 / (_1_0 + cr)
321
+ t = (s1 - s2)**2 / (_1_0 - cr)
322
+ x = s + t
323
+ y = s - t
324
+
325
+ s = 8 * r**2 / sr
326
+ a = 64 * r + s * cr * 2 # 16 * r**2 / tan(r)
327
+ d = 48 * sr + s # 8 * r**2 / tan(r)
328
+ b = -2 * d
329
+ e = 30 * s2r
330
+
331
+ c = fdot_(30, r, cr, s, e, _0_5) # 8 * r**2 / tan(r)
332
+ t = fdot_( a, x, b, y, e, y**2, -c, x**2, d, x * y) * _0_125
333
+ r += fdot_(-r, x, sr, y * 3, t, f) * f
334
+ else:
335
+ raise _ValueError(corr=corr)
327
336
  return r
328
337
 
329
338
 
330
- def cosineLaw(lat1, lon1, lat2, lon2, radius=R_M, wrap=False):
331
- '''Compute the distance between two points using the U{spherical Law of Cosines
332
- <https://www.Movable-Type.co.UK/scripts/latlong.html#cosine-law>} formula.
333
-
334
- @arg lat1: Start latitude (C{degrees}).
335
- @arg lon1: Start longitude (C{degrees}).
336
- @arg lat2: End latitude (C{degrees}).
337
- @arg lon2: End longitude (C{degrees}).
338
- @kwarg radius: Mean earth radius (C{meter}), datum (L{Datum})
339
- or ellipsoid (L{Ellipsoid}, L{Ellipsoid2} or
340
- L{a_f2Tuple}) to use.
341
- @kwarg wrap: If C{True}, wrap or I{normalize} and B{C{lat2}}
342
- and B{C{lon2}} (C{bool}).
343
-
344
- @return: Distance (C{meter}, same units as B{C{radius}} or the
345
- ellipsoid or datum axes).
346
-
347
- @raise TypeError: Invalid B{C{radius}}.
348
-
349
- @see: Functions L{cosineLaw_}, L{cosineAndoyerLambert},
350
- L{cosineForsytheAndoyerLambert}, L{equirectangular}, L{euclidean},
351
- L{flatLocal}/L{hubeny}, L{flatPolar}, L{haversine}, L{thomas} and
352
- L{vincentys} and method L{Ellipsoid.distance2}.
353
-
354
- @note: See note at function L{vincentys_}.
355
- '''
356
- return _dS(cosineLaw_, radius, wrap, lat1, lon1, lat2, lon2)
357
-
358
-
359
- def cosineLaw_(phi2, phi1, lam21):
360
- '''Compute the I{angular} distance between two points using the U{spherical Law of
361
- Cosines<https://www.Movable-Type.co.UK/scripts/latlong.html#cosine-law>} formula.
362
-
363
- @arg phi2: End latitude (C{radians}).
364
- @arg phi1: Start latitude (C{radians}).
365
- @arg lam21: Longitudinal delta, M{end-start} (C{radians}).
366
-
367
- @return: Angular distance (C{radians}).
368
-
369
- @see: Functions L{cosineLaw}, L{cosineAndoyerLambert_},
370
- L{cosineForsytheAndoyerLambert_}, L{euclidean_},
371
- L{flatLocal_}/L{hubeny_}, L{flatPolar_}, L{haversine_},
372
- L{thomas_} and L{vincentys_}.
373
-
374
- @note: See note at function L{vincentys_}.
375
- '''
376
- return _sincosa6(phi2, phi1, lam21)[4]
377
-
378
-
379
339
  def _d3(wrap, lat1, lon1, lat2, lon2):
380
- '''(INTERNAL) Helper for _dE, _dS and _eA.
340
+ '''(INTERNAL) Helper for _dE, _dS, ....
381
341
  '''
382
342
  if wrap:
383
343
  d_lon, lat2, _ = _Wrap.latlon3(lon1, lat2, lon2, wrap)
384
344
  return radians(lat2), Phid(lat1=lat1), radians(d_lon)
385
345
  else: # for backward compaibility
386
- return Phid(lat2=lat2), Phid(lat1=lat1), Phid(d_lon=lon2 - lon1)
346
+ return Phid(lat2=lat2), Phid(lat1=lat1), radians(lon2 - lon1)
387
347
 
388
348
 
389
- def _dE(func_, earth, *wrap_lls):
349
+ def _dE(fun_, earth, wrap, *lls, **corr):
390
350
  '''(INTERNAL) Helper for ellipsoidal distances.
391
351
  '''
392
- E = _ellipsoidal(earth, func_)
393
- r = func_(*_d3(*wrap_lls), datum=E)
352
+ E = _ellipsoidal(earth, fun_)
353
+ r = fun_(*_d3(wrap, *lls), datum=E, **corr)
394
354
  return r * E.a
395
355
 
396
356
 
397
- def _dS(func_, radius, *wrap_lls, **adjust):
357
+ def _dS(fun_, radius, wrap, *lls, **adjust):
398
358
  '''(INTERNAL) Helper for spherical distances.
399
359
  '''
400
- r = func_(*_d3(*wrap_lls), **adjust)
360
+ r = fun_(*_d3(wrap, *lls), **adjust)
401
361
  if radius is not R_M:
402
- _, lat1, _, lat2, _ = wrap_lls
362
+ try: # datum?
363
+ radius = radius.ellipsoid.R1
364
+ except AttributeError:
365
+ pass # scalar?
366
+ lat1, _, lat2, _ = lls
403
367
  radius = _mean_radius(radius, lat1, lat2)
404
368
  return r * radius
405
369
 
406
370
 
407
- def _eA(excess_, radius, *wrap_lls):
408
- '''(INTERNAL) Helper for spherical excess or area.
409
- '''
410
- r = excess_(*_d3(*wrap_lls))
411
- if radius:
412
- _, lat1, _, lat2, _ = wrap_lls
413
- r *= _mean_radius(radius, lat1, lat2)**2
414
- return r
415
-
416
-
417
371
  def _ellipsoidal(earth, where):
418
372
  '''(INTERNAL) Helper for distances.
419
373
  '''
@@ -424,7 +378,7 @@ def _ellipsoidal(earth, where):
424
378
 
425
379
 
426
380
  def equirectangular(lat1, lon1, lat2, lon2, radius=R_M, **adjust_limit_wrap):
427
- '''Compute the distance between two points using the U{Equirectangular Approximation
381
+ '''Approximate the distance between two points using the U{Equirectangular Approximation
428
382
  / Projection<https://www.Movable-Type.co.UK/scripts/latlong.html#equirectangular>}.
429
383
 
430
384
  @arg lat1: Start latitude (C{degrees}).
@@ -433,30 +387,33 @@ def equirectangular(lat1, lon1, lat2, lon2, radius=R_M, **adjust_limit_wrap):
433
387
  @arg lon2: End longitude (C{degrees}).
434
388
  @kwarg radius: Mean earth radius (C{meter}), datum (L{Datum}) or ellipsoid
435
389
  (L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}).
436
- @kwarg adjust_limit_wrap: Optional keyword arguments for function L{equirectangular4}.
390
+ @kwarg adjust_limit_wrap: Optionally, keyword arguments for function L{equirectangular4}.
437
391
 
438
- @return: Distance (C{meter}, same units as B{C{radius}} or the ellipsoid or datum axes).
392
+ @return: Distance (C{meter}, same units as B{C{radius}} or the datum's
393
+ ellipsoid axes).
439
394
 
440
395
  @raise TypeError: Invalid B{C{radius}}.
441
396
 
442
397
  @see: Function L{equirectangular4} for more details, the available B{C{options}},
443
398
  errors, restrictions and other, approximate or accurate distance functions.
444
399
  '''
445
- d = sqrt(equirectangular4(Lat(lat1=lat1), Lon(lon1=lon1),
446
- Lat(lat2=lat2), Lon(lon2=lon2),
447
- **adjust_limit_wrap).distance2) # PYCHOK 4 vs 2-3
448
- return degrees2m(d, radius=_mean_radius(radius, lat1, lat2))
400
+ r = _mean_radius(radius, lat1, lat2)
401
+ t = equirectangular4(Lat(lat1=lat1), Lon(lon1=lon1),
402
+ Lat(lat2=lat2), Lon(lon2=lon2),
403
+ **adjust_limit_wrap) # PYCHOK 4 vs 2-3
404
+ return degrees2m(sqrt(t.distance2), radius=r)
449
405
 
450
406
 
451
407
  def _equirectangular(lat1, lon1, lat2, lon2, **adjust_limit_wrap):
452
- '''(INTERNAL) Helper for the L{frechet._FrechetMeterRadians}
453
- and L{hausdorff._HausdorffMeterRedians} classes.
408
+ '''(INTERNAL) Helper for classes L{frechet._FrechetMeterRadians} and
409
+ L{hausdorff._HausdorffMeterRedians}.
454
410
  '''
455
- return equirectangular4(lat1, lon1, lat2, lon2, **adjust_limit_wrap).distance2 * _RADIANS2
411
+ t = equirectangular4(lat1, lon1, lat2, lon2, **adjust_limit_wrap)
412
+ return t.distance2 * _RADIANS2
456
413
 
457
414
 
458
415
  def equirectangular4(lat1, lon1, lat2, lon2, adjust=True, limit=45, wrap=False):
459
- '''Compute the distance between two points using the U{Equirectangular Approximation
416
+ '''Approximate the distance between two points using the U{Equirectangular Approximation
460
417
  / Projection<https://www.Movable-Type.co.UK/scripts/latlong.html#equirectangular>}.
461
418
 
462
419
  This approximation is valid for short distance of several hundred Km or Miles, see
@@ -466,26 +423,28 @@ def equirectangular4(lat1, lon1, lat2, lon2, adjust=True, limit=45, wrap=False):
466
423
  @arg lon1: Start longitude (C{degrees}).
467
424
  @arg lat2: End latitude (C{degrees}).
468
425
  @arg lon2: End longitude (C{degrees}).
469
- @kwarg adjust: Adjust the wrapped, unrolled longitudinal delta by the cosine of the mean
470
- latitude (C{bool}).
426
+ @kwarg adjust: Adjust the wrapped, unrolled longitudinal delta by the cosine of the
427
+ mean latitude (C{bool}).
471
428
  @kwarg limit: Optional limit for lat- and longitudinal deltas (C{degrees}) or C{None}
472
429
  or C{0} for unlimited.
473
430
  @kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{lat2}} and B{C{lon2}}
474
431
  (C{bool}).
475
432
 
476
- @return: A L{Distance4Tuple}C{(distance2, delta_lat, delta_lon, unroll_lon2)}
477
- in C{degrees squared}.
433
+ @return: A L{Distance4Tuple}C{(distance2, delta_lat, delta_lon, unroll_lon2)} with
434
+ C{distance2} in C{degrees squared}.
478
435
 
479
- @raise LimitError: If the lat- and/or longitudinal delta exceeds the B{C{-limit..limit}}
436
+ @raise LimitError: The lat- or longitudinal delta exceeds the B{C{-limit..limit}}
480
437
  range and L{limiterrors<pygeodesy.limiterrors>} is C{True}.
481
438
 
482
439
  @see: U{Local, flat earth approximation<https://www.EdWilliams.org/avform.htm#flat>},
483
- functions L{equirectangular}, L{cosineAndoyerLambert}, L{cosineForsytheAndoyerLambert},
484
- L{cosineLaw}, L{euclidean}, L{flatLocal}/L{hubeny}, L{flatPolar}, L{haversine},
485
- L{thomas} and L{vincentys} and methods L{Ellipsoid.distance2}, C{LatLon.distanceTo*}
486
- and C{LatLon.equirectangularTo}.
440
+ functions L{equirectangular}, L{cosineLaw}, L{euclidean}, L{flatLocal} /
441
+ L{hubeny}, L{flatPolar}, L{haversine}, L{thomas} and L{vincentys} and methods
442
+ L{Ellipsoid.distance2}, C{LatLon.distanceTo*} and C{LatLon.equirectangularTo}.
487
443
  '''
488
- d_lon, lat2, ulon2 = _Wrap.latlon3(lon1, lat2, lon2, wrap)
444
+ if wrap:
445
+ d_lon, lat2, ulon2 = _Wrap.latlon3(lon1, lat2, lon2, wrap)
446
+ else:
447
+ d_lon, ulon2 = (lon2 - lon1), lon2
489
448
  d_lat = lat2 - lat1
490
449
 
491
450
  if limit and limit > 0 and limiterrors():
@@ -510,25 +469,24 @@ def euclidean(lat1, lon1, lat2, lon2, radius=R_M, adjust=True, wrap=False):
510
469
  @arg lon1: Start longitude (C{degrees}).
511
470
  @arg lat2: End latitude (C{degrees}).
512
471
  @arg lon2: End longitude (C{degrees}).
513
- @kwarg radius: Mean earth radius (C{meter}), datum (L{Datum})
514
- or ellipsoid (L{Ellipsoid}, L{Ellipsoid2} or
515
- L{a_f2Tuple}) to use.
516
- @kwarg adjust: Adjust the longitudinal delta by the cosine of
517
- the mean latitude (C{bool}).
518
- @kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{lat2}}
519
- and B{C{lon2}} (C{bool}).
472
+ @kwarg radius: Mean earth radius (C{meter}), datum (L{Datum}) or ellipsoid
473
+ (L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}) to use.
474
+ @kwarg adjust: Adjust the longitudinal delta by the cosine of the mean
475
+ latitude (C{bool}).
476
+ @kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{lat2}} and
477
+ B{C{lon2}} (C{bool}).
520
478
 
521
- @return: Distance (C{meter}, same units as B{C{radius}} or the
522
- ellipsoid or datum axes).
479
+ @return: Distance (C{meter}, same units as B{C{radius}} or the ellipsoid
480
+ or datum axes).
523
481
 
524
482
  @raise TypeError: Invalid B{C{radius}}.
525
483
 
526
484
  @see: U{Distance between two (spherical) points
527
485
  <https://www.EdWilliams.org/avform.htm#Dist>}, functions L{euclid},
528
- L{euclidean_}, L{cosineAndoyerLambert}, L{cosineForsytheAndoyerLambert},
529
- L{cosineLaw}, L{equirectangular}, L{flatLocal}/L{hubeny}, L{flatPolar},
530
- L{haversine}, L{thomas} and L{vincentys} and methods L{Ellipsoid.distance2},
531
- C{LatLon.distanceTo*} and C{LatLon.equirectangularTo}.
486
+ L{euclidean_}, L{cosineLaw}, L{equirectangular}, L{flatLocal} /
487
+ L{hubeny}, L{flatPolar}, L{haversine}, L{thomas} and L{vincentys}
488
+ and methods L{Ellipsoid.distance2}, C{LatLon.distanceTo*} and
489
+ C{LatLon.equirectangularTo}.
532
490
  '''
533
491
  return _dS(euclidean_, radius, wrap, lat1, lon1, lat2, lon2, adjust=adjust)
534
492
 
@@ -539,15 +497,13 @@ def euclidean_(phi2, phi1, lam21, adjust=True):
539
497
  @arg phi2: End latitude (C{radians}).
540
498
  @arg phi1: Start latitude (C{radians}).
541
499
  @arg lam21: Longitudinal delta, M{end-start} (C{radians}).
542
- @kwarg adjust: Adjust the longitudinal delta by the cosine
543
- of the mean latitude (C{bool}).
500
+ @kwarg adjust: Adjust the longitudinal delta by the cosine of the mean
501
+ latitude (C{bool}).
544
502
 
545
503
  @return: Angular distance (C{radians}).
546
504
 
547
- @see: Functions L{euclid}, L{euclidean}, L{cosineAndoyerLambert_},
548
- L{cosineForsytheAndoyerLambert_}, L{cosineLaw_},
549
- L{flatLocal_}/L{hubeny_}, L{flatPolar_}, L{haversine_},
550
- L{thomas_} and L{vincentys_}.
505
+ @see: Functions L{euclid}, L{euclidean}, L{cosineLaw_}, L{flatLocal_} /
506
+ L{hubeny_}, L{flatPolar_}, L{haversine_}, L{thomas_} and L{vincentys_}.
551
507
  '''
552
508
  if adjust:
553
509
  lam21 *= _scale_rad(phi2, phi1)
@@ -574,7 +530,9 @@ def excessAbc_(A, b, c):
574
530
  c = Radians_(c=c) * _0_5
575
531
 
576
532
  sA, cA, sb, cb, sc, cc = sincos2_(A, b, c)
577
- return atan2(sA * sb * sc, cb * cc + cA * sb * sc) * _2_0
533
+ s = sA * sb * sc
534
+ c = cA * sb * sc + cc * cb
535
+ return atan2(s, c) * _2_0
578
536
 
579
537
 
580
538
  def excessCagnoli_(a, b, c):
@@ -596,11 +554,11 @@ def excessCagnoli_(a, b, c):
596
554
  b = Radians_(b=b)
597
555
  c = Radians_(c=c)
598
556
 
599
- s = fsumf_(a, b, c) * _0_5
600
- _s = sin
601
- r = _s(s) * _s(s - a) * _s(s - b) * _s(s - c)
602
- c = cos(a * _0_5) * cos(b * _0_5) * cos(c * _0_5)
603
- r = asin(sqrt(r) * _0_5 / c) if c and r > 0 else _0_0
557
+ r = _maprod(cos, a * _0_5, b * _0_5, c * _0_5)
558
+ if r:
559
+ s = fsumf_(a, b, c) * _0_5
560
+ t = _maprod(sin, s, s - a, s - b, s - c)
561
+ r = asin1(sqrt(t) * _0_5 / r) if t > 0 else _0_0
604
562
  return Radians(Cagnoli=r * _2_0)
605
563
 
606
564
 
@@ -619,9 +577,10 @@ def excessGirard_(A, B, C):
619
577
  @see: Function L{excessLHuilier_} and U{Spherical trigonometry
620
578
  <https://WikiPedia.org/wiki/Spherical_trigonometry>}.
621
579
  '''
622
- return Radians(Girard=fsumf_(Radians_(A=A),
623
- Radians_(B=B),
624
- Radians_(C=C), -PI))
580
+ r = fsumf_(Radians_(A=A),
581
+ Radians_(B=B),
582
+ Radians_(C=C), -PI)
583
+ return Radians(Girard=r)
625
584
 
626
585
 
627
586
  def excessLHuilier_(a, b, c):
@@ -643,10 +602,9 @@ def excessLHuilier_(a, b, c):
643
602
  b = Radians_(b=b)
644
603
  c = Radians_(c=c)
645
604
 
646
- s = fsumf_(a, b, c) * _0_5
647
- _t = tan_2
648
- r = _t(s) * _t(s - a) * _t(s - b) * _t(s - c)
649
- r = atan(sqrt(r)) if r > 0 else _0_0
605
+ s = fsumf_(a, b, c) * _0_5
606
+ r = _maprod(tan_2, s, s - a, s - b, s - c)
607
+ r = atan(sqrt(r)) if r > 0 else _0_0
650
608
  return Radians(LHuilier=r * _4_0)
651
609
 
652
610
 
@@ -660,11 +618,10 @@ def excessKarney(lat1, lon1, lat2, lon2, radius=R_M, wrap=False):
660
618
  @arg lon1: Start longitude (C{degrees}).
661
619
  @arg lat2: End latitude (C{degrees}).
662
620
  @arg lon2: End longitude (C{degrees}).
663
- @kwarg radius: Mean earth radius (C{meter}), datum (L{Datum})
664
- or ellipsoid (L{Ellipsoid}, L{Ellipsoid2} or
665
- L{a_f2Tuple}) or C{None}.
666
- @kwarg wrap: If C{True}, wrap or I{normalize} and unroll
667
- B{C{lat2}} and B{C{lon2}} (C{bool}).
621
+ @kwarg radius: Mean earth radius (C{meter}), datum (L{Datum}) or ellipsoid
622
+ (L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}) or C{None}.
623
+ @kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{lat2}} and
624
+ B{C{lon2}} (C{bool}).
668
625
 
669
626
  @return: Surface area, I{signed} (I{square} C{meter} or the same units as
670
627
  B{C{radius}} I{squared}) or the I{spherical excess} (C{radians})
@@ -678,7 +635,10 @@ def excessKarney(lat1, lon1, lat2, lon2, radius=R_M, wrap=False):
678
635
 
679
636
  @see: Functions L{excessKarney_} and L{excessQuad}.
680
637
  '''
681
- return _eA(excessKarney_, radius, wrap, lat1, lon1, lat2, lon2)
638
+ r = excessKarney_(*_d3(wrap, lat1, lon1, lat2, lon2))
639
+ if radius:
640
+ r *= _mean_radius(radius, lat1, lat2)**2
641
+ return r
682
642
 
683
643
 
684
644
  def excessKarney_(phi2, phi1, lam21):
@@ -707,12 +667,11 @@ def excessKarney_(phi2, phi1, lam21):
707
667
  #
708
668
  # where E is the spherical excess of the trapezium obtained by extending
709
669
  # the edge to the equator-circle vector for each edge (see also ***).
710
- _t = tan_2
711
- t2 = _t(phi2)
712
- t1 = _t(phi1)
713
- t = _t(lam21, lam21=None)
714
- return Radians(Karney=atan2(t * (t1 + t2),
715
- _1_0 + (t1 * t2)) * _2_0)
670
+ t2 = tan_2(phi2)
671
+ t1 = tan_2(phi1)
672
+ c = (t1 * t2) + _1_0
673
+ s = (t1 + t2) * tan_2(lam21, lam21=None)
674
+ return Radians(Karney=atan2(s, c) * _2_0)
716
675
 
717
676
 
718
677
  # ***) Original post no longer available, following is a copy of the main part
@@ -762,11 +721,10 @@ def excessQuad(lat1, lon1, lat2, lon2, radius=R_M, wrap=False):
762
721
  @arg lon1: Start longitude (C{degrees}).
763
722
  @arg lat2: End latitude (C{degrees}).
764
723
  @arg lon2: End longitude (C{degrees}).
765
- @kwarg radius: Mean earth radius (C{meter}), datum (L{Datum})
766
- or ellipsoid (L{Ellipsoid}, L{Ellipsoid2} or
767
- L{a_f2Tuple}) or C{None}.
768
- @kwarg wrap: If C{True}, wrap or I{normalize} and unroll
769
- B{C{lat2}} and B{C{lon2}} (C{bool}).
724
+ @kwarg radius: Mean earth radius (C{meter}), datum (L{Datum}) or ellipsoid
725
+ (L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}) or C{None}.
726
+ @kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{lat2}} and
727
+ B{C{lon2}} (C{bool}).
770
728
 
771
729
  @return: Surface area, I{signed} (I{square} C{meter} or the same units as
772
730
  B{C{radius}} I{squared}) or the I{spherical excess} (C{radians})
@@ -778,7 +736,10 @@ def excessQuad(lat1, lon1, lat2, lon2, radius=R_M, wrap=False):
778
736
 
779
737
  @see: Function L{excessQuad_} and L{excessKarney}.
780
738
  '''
781
- return _eA(excessQuad_, radius, wrap, lat1, lon1, lat2, lon2)
739
+ r = excessQuad_(*_d3(wrap, lat1, lon1, lat2, lon2))
740
+ if radius:
741
+ r *= _mean_radius(radius, lat1, lat2)**2
742
+ return r
782
743
 
783
744
 
784
745
  def excessQuad_(phi2, phi1, lam21):
@@ -794,9 +755,9 @@ def excessQuad_(phi2, phi1, lam21):
794
755
  @see: Function L{excessQuad} and U{Spherical trigonometry
795
756
  <https://WikiPedia.org/wiki/Spherical_trigonometry>}.
796
757
  '''
797
- s = sin((phi2 + phi1) * _0_5)
798
758
  c = cos((phi2 - phi1) * _0_5)
799
- return Radians(Quad=atan2(tan_2(lam21) * s, c) * _2_0)
759
+ s = sin((phi2 + phi1) * _0_5) * tan_2(lam21)
760
+ return Radians(Quad=atan2(s, c) * _2_0)
800
761
 
801
762
 
802
763
  def flatLocal(lat1, lon1, lat2, lon2, datum=_WGS84, scaled=True, wrap=False):
@@ -809,30 +770,28 @@ def flatLocal(lat1, lon1, lat2, lon2, datum=_WGS84, scaled=True, wrap=False):
809
770
  @arg lon1: Start longitude (C{degrees}).
810
771
  @arg lat2: End latitude (C{degrees}).
811
772
  @arg lon2: End longitude (C{degrees}).
812
- @kwarg datum: Datum (L{Datum}) or ellipsoid (L{Ellipsoid},
813
- L{Ellipsoid2} or L{a_f2Tuple}) to use.
814
- @kwarg scaled: Scale prime_vertical by C{cos(B{phi})} (C{bool}),
815
- see method L{pygeodesy.Ellipsoid.roc2_}.
816
- @kwarg wrap: If C{True}, wrap or I{normalize} and unroll
817
- B{C{lat2}} and B{C{lon2}} (C{bool}).
818
-
819
- @return: Distance (C{meter}, same units as the B{C{datum}}'s
820
- ellipsoid axes).
773
+ @kwarg datum: Datum (L{Datum}) or ellipsoid (L{Ellipsoid}, L{Ellipsoid2}
774
+ or L{a_f2Tuple}) to use.
775
+ @kwarg scaled: Scale prime_vertical by C{cos(B{phi})} (C{bool}), see
776
+ method L{pygeodesy.Ellipsoid.roc2_}.
777
+ @kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{lat2}} and
778
+ B{C{lon2}} (C{bool}).
779
+
780
+ @return: Distance (C{meter}, same units as the B{C{datum}}'s or ellipsoid axes).
821
781
 
822
782
  @raise TypeError: Invalid B{C{datum}}.
823
783
 
824
- @note: The meridional and prime_vertical radii of curvature
825
- are taken and scaled at the mean of both latitude.
784
+ @note: The meridional and prime_vertical radii of curvature are taken and
785
+ scaled at the mean of both latitude.
826
786
 
827
- @see: Functions L{flatLocal_} or L{hubeny_}, L{cosineLaw}, L{flatPolar},
828
- L{cosineAndoyerLambert}, L{cosineForsytheAndoyerLambert},
829
- L{equirectangular}, L{euclidean}, L{haversine}, L{thomas},
830
- L{vincentys}, method L{Ellipsoid.distance2} and U{local, flat
831
- earth approximation<https://www.EdWilliams.org/avform.htm#flat>}.
787
+ @see: Functions L{flatLocal_} or L{hubeny_}, L{cosineLaw}, L{equirectangular},
788
+ L{euclidean}, L{flatPolar}, L{haversine}, L{thomas} and L{vincentys}, method
789
+ L{Ellipsoid.distance2} and U{local, flat earth approximation
790
+ <https://www.EdWilliams.org/avform.htm#flat>}.
832
791
  '''
792
+ t = _d3(wrap, lat1, lon1, lat2, lon2)
833
793
  E = _ellipsoidal(datum, flatLocal)
834
- return E._hubeny_2(*_d3(wrap, lat1, lon1, lat2, lon2),
835
- scaled=scaled, squared=False) * E.a
794
+ return E._hubeny_2(*t, scaled=scaled, squared=False) * E.a
836
795
 
837
796
  hubeny = flatLocal # PYCHOK for Karl Hubeny
838
797
 
@@ -846,23 +805,21 @@ def flatLocal_(phi2, phi1, lam21, datum=_WGS84, scaled=True):
846
805
  @arg phi2: End latitude (C{radians}).
847
806
  @arg phi1: Start latitude (C{radians}).
848
807
  @arg lam21: Longitudinal delta, M{end-start} (C{radians}).
849
- @kwarg datum: Datum (L{Datum}) or ellipsoid (L{Ellipsoid},
850
- L{Ellipsoid2} or L{a_f2Tuple}) to use.
851
- @kwarg scaled: Scale prime_vertical by C{cos(B{phi})} (C{bool}),
852
- see method L{pygeodesy.Ellipsoid.roc2_}.
808
+ @kwarg datum: Datum (L{Datum}) or ellipsoid (L{Ellipsoid}, L{Ellipsoid2}
809
+ or L{a_f2Tuple}) to use.
810
+ @kwarg scaled: Scale prime_vertical by C{cos(B{phi})} (C{bool}), see
811
+ method L{pygeodesy.Ellipsoid.roc2_}.
853
812
 
854
813
  @return: Angular distance (C{radians}).
855
814
 
856
815
  @raise TypeError: Invalid B{C{datum}}.
857
816
 
858
- @note: The meridional and prime_vertical radii of curvature
859
- are taken and scaled I{at the mean of both latitude}.
817
+ @note: The meridional and prime_vertical radii of curvature are taken and
818
+ scaled I{at the mean of both latitude}.
860
819
 
861
- @see: Functions L{flatLocal} or L{hubeny}, L{cosineAndoyerLambert_},
862
- L{cosineForsytheAndoyerLambert_}, L{cosineLaw_}, L{flatPolar_},
863
- L{euclidean_}, L{haversine_}, L{thomas_} and L{vincentys_} and
864
- U{local, flat earth approximation
865
- <https://www.EdWilliams.org/avform.htm#flat>}.
820
+ @see: Functions L{flatLocal} or L{hubeny}, L{cosineLaw_}, L{flatPolar_},
821
+ L{euclidean_}, L{haversine_}, L{thomas_} and L{vincentys_} and U{local,
822
+ flat earth approximation<https://www.EdWilliams.org/avform.htm#flat>}.
866
823
  '''
867
824
  E = _ellipsoidal(datum, flatLocal_)
868
825
  return E._hubeny_2(phi2, phi1, lam21, scaled=scaled, squared=False)
@@ -871,40 +828,34 @@ hubeny_ = flatLocal_ # PYCHOK for Karl Hubeny
871
828
 
872
829
 
873
830
  def flatPolar(lat1, lon1, lat2, lon2, radius=R_M, wrap=False):
874
- '''Compute the distance between two (spherical) points using
875
- the U{polar coordinate flat-Earth <https://WikiPedia.org/wiki/
876
- Geographical_distance#Polar_coordinate_flat-Earth_formula>}
877
- formula.
831
+ '''Compute the distance between two (spherical) points using the U{polar
832
+ coordinate flat-Earth<https://WikiPedia.org/wiki/Geographical_distance
833
+ #Polar_coordinate_flat-Earth_formula>} formula.
878
834
 
879
835
  @arg lat1: Start latitude (C{degrees}).
880
836
  @arg lon1: Start longitude (C{degrees}).
881
837
  @arg lat2: End latitude (C{degrees}).
882
838
  @arg lon2: End longitude (C{degrees}).
883
- @kwarg radius: Mean earth radius (C{meter}), datum (L{Datum})
884
- or ellipsoid (L{Ellipsoid}, L{Ellipsoid2} or
885
- L{a_f2Tuple}) to use.
886
- @kwarg wrap: If C{True}, wrap or I{normalize} and B{C{lat2}}
887
- and B{C{lon2}} (C{bool}).
839
+ @kwarg radius: Mean earth radius (C{meter}), datum (L{Datum}) or ellipsoid
840
+ (L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}) to use.
841
+ @kwarg wrap: If C{True}, wrap or I{normalize} and B{C{lat2}} and B{C{lon2}}
842
+ (C{bool}).
888
843
 
889
- @return: Distance (C{meter}, same units as B{C{radius}} or the
890
- ellipsoid or datum axes).
844
+ @return: Distance (C{meter}, same units as B{C{radius}} or the datum's or
845
+ ellipsoid axes).
891
846
 
892
847
  @raise TypeError: Invalid B{C{radius}}.
893
848
 
894
- @see: Functions L{flatPolar_}, L{cosineAndoyerLambert},
895
- L{cosineForsytheAndoyerLambert},L{cosineLaw},
896
- L{flatLocal}/L{hubeny}, L{equirectangular},
897
- L{euclidean}, L{haversine}, L{thomas} and
898
- L{vincentys}.
849
+ @see: Functions L{flatPolar_}, L{cosineLaw}, L{flatLocal} / L{hubeny},
850
+ L{equirectangular}, L{euclidean}, L{haversine}, L{thomas} and L{vincentys}.
899
851
  '''
900
852
  return _dS(flatPolar_, radius, wrap, lat1, lon1, lat2, lon2)
901
853
 
902
854
 
903
855
  def flatPolar_(phi2, phi1, lam21):
904
- '''Compute the I{angular} distance between two (spherical) points
905
- using the U{polar coordinate flat-Earth<https://WikiPedia.org/wiki/
906
- Geographical_distance#Polar_coordinate_flat-Earth_formula>}
907
- formula.
856
+ '''Compute the I{angular} distance between two (spherical) points using the
857
+ U{polar coordinate flat-Earth<https://WikiPedia.org/wiki/Geographical_distance
858
+ #Polar_coordinate_flat-Earth_formula>} formula.
908
859
 
909
860
  @arg phi2: End latitude (C{radians}).
910
861
  @arg phi1: Start latitude (C{radians}).
@@ -912,10 +863,8 @@ def flatPolar_(phi2, phi1, lam21):
912
863
 
913
864
  @return: Angular distance (C{radians}).
914
865
 
915
- @see: Functions L{flatPolar}, L{cosineAndoyerLambert_},
916
- L{cosineForsytheAndoyerLambert_}, L{cosineLaw_},
917
- L{euclidean_}, L{flatLocal_}/L{hubeny_}, L{haversine_},
918
- L{thomas_} and L{vincentys_}.
866
+ @see: Functions L{flatPolar}, L{cosineLaw_}, L{euclidean_}, L{flatLocal_} /
867
+ L{hubeny_}, L{haversine_}, L{thomas_} and L{vincentys_}.
919
868
  '''
920
869
  a = fabs(PI_2 - phi1) # co-latitude
921
870
  b = fabs(PI_2 - phi2) # co-latitude
@@ -957,14 +906,14 @@ def hartzell(pov, los=False, earth=_WGS84, **name_LatLon_and_kwds):
957
906
  or C{None} to point to the center of the earth.
958
907
  @kwarg earth: The earth model (L{Datum}, L{Ellipsoid}, L{Ellipsoid2},
959
908
  L{a_f2Tuple} or a C{scalar} earth radius in C{meter}).
960
- @kwarg name_LatLon_and_kwds: Optional, overriding C{B{name}="hartzell"}
961
- (C{str}), class C{B{LatLon}=None} to return the intersection
962
- plus additional C{LatLon} keyword arguments, include the
963
- B{C{datum}} if different and to convert from B{C{earth}}.
909
+ @kwarg name_LatLon_and_kwds: Optional C{B{name}="hartzell"} (C{str}), class
910
+ C{B{LatLon}=None} to return the intersection and optionally,
911
+ additional C{LatLon} keyword arguments, include the B{C{datum}}
912
+ if different from and to convert from B{C{earth}}.
964
913
 
965
- @return: The intersection (L{Vector3d}, B{C{pov}}'s C{cartesian type} or the
966
- given B{C{LatLon}} instance) with attribute C{height} set to the
967
- distance to the B{C{pov}}.
914
+ @return: The intersection (L{Vector3d}, B{C{pov}}'s C{cartesian type} or
915
+ the given B{C{LatLon}} instance) with attribute C{height} set to
916
+ the distance to the B{C{pov}}.
968
917
 
969
918
  @raise IntersectionError: Invalid B{C{pov}} or B{C{pov}} inside the earth or
970
919
  invalid B{C{los}} or B{C{los}} points outside or
@@ -973,50 +922,46 @@ def hartzell(pov, los=False, earth=_WGS84, **name_LatLon_and_kwds):
973
922
  @raise TypeError: Invalid B{C{earth}}, C{ellipsoid} or C{datum}.
974
923
 
975
924
  @see: Class L{Los}, functions L{tyr3d} and L{hartzell4} and methods
976
- L{Ellipsoid.hartzell4} and any C{Cartesian.hartzell} and C{LatLon.hartzell}.
925
+ L{Ellipsoid.hartzell4}, any C{Cartesian.hartzell} and C{LatLon.hartzell}.
977
926
  '''
978
- n, LatLon_and_kwds = _name2__(name_LatLon_and_kwds, name__=hartzell)
927
+ n, kwds = _name2__(name_LatLon_and_kwds, name__=hartzell)
979
928
  try:
980
929
  D = _spherical_datum(earth, name__=hartzell)
981
930
  r, h, i = _MODS.triaxials._hartzell3(pov, los, D.ellipsoid._triaxial)
982
931
 
983
932
  C = _MODS.cartesianBase.CartesianBase
984
- if LatLon_and_kwds:
933
+ if kwds:
985
934
  c = C(r, datum=D)
986
- r = c.toLatLon(**_xkwds(LatLon_and_kwds, height=h))
935
+ r = c.toLatLon(**_xkwds(kwds, height=h))
987
936
  elif isinstance(r, C):
988
937
  r.height = h
989
938
  if i:
990
939
  r._iteration = i
991
940
  except Exception as x:
992
- raise IntersectionError(pov=pov, los=los, earth=earth, cause=x,
993
- **LatLon_and_kwds)
941
+ raise IntersectionError(pov=pov, los=los, earth=earth, cause=x, **kwds)
994
942
  return _xnamed(r, n) if n else r
995
943
 
996
944
 
997
945
  def haversine(lat1, lon1, lat2, lon2, radius=R_M, wrap=False):
998
- '''Compute the distance between two (spherical) points using the
999
- U{Haversine<https://www.Movable-Type.co.UK/scripts/latlong.html>}
1000
- formula.
946
+ '''Compute the distance between two (spherical) points using the U{Haversine
947
+ <https://www.Movable-Type.co.UK/scripts/latlong.html>} formula.
1001
948
 
1002
949
  @arg lat1: Start latitude (C{degrees}).
1003
950
  @arg lon1: Start longitude (C{degrees}).
1004
951
  @arg lat2: End latitude (C{degrees}).
1005
952
  @arg lon2: End longitude (C{degrees}).
1006
- @kwarg radius: Mean earth radius (C{meter}), datum (L{Datum})
1007
- or ellipsoid (L{Ellipsoid}, L{Ellipsoid2} or
1008
- L{a_f2Tuple}) to use.
1009
- @kwarg wrap: If C{True}, wrap or I{normalize} and unroll
1010
- B{C{lat2}} and B{C{lon2}} (C{bool}).
953
+ @kwarg radius: Mean earth radius (C{meter}), datum (L{Datum}) or ellipsoid
954
+ (L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}) to use.
955
+ @kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{lat2}} and
956
+ B{C{lon2}} (C{bool}).
1011
957
 
1012
958
  @return: Distance (C{meter}, same units as B{C{radius}}).
1013
959
 
1014
960
  @raise TypeError: Invalid B{C{radius}}.
1015
961
 
1016
962
  @see: U{Distance between two (spherical) points
1017
- <https://www.EdWilliams.org/avform.htm#Dist>}, functions
1018
- L{cosineLaw}, L{cosineAndoyerLambert}, L{cosineForsytheAndoyerLambert},
1019
- L{equirectangular}, L{euclidean}, L{flatLocal}/L{hubeny}, L{flatPolar},
963
+ <https://www.EdWilliams.org/avform.htm#Dist>}, functions L{cosineLaw},
964
+ L{equirectangular}, L{euclidean}, L{flatLocal} / L{hubeny}, L{flatPolar},
1020
965
  L{thomas} and L{vincentys} and methods L{Ellipsoid.distance2},
1021
966
  C{LatLon.distanceTo*} and C{LatLon.equirectangularTo}.
1022
967
 
@@ -1026,9 +971,8 @@ def haversine(lat1, lon1, lat2, lon2, radius=R_M, wrap=False):
1026
971
 
1027
972
 
1028
973
  def haversine_(phi2, phi1, lam21):
1029
- '''Compute the I{angular} distance between two (spherical) points
1030
- using the U{Haversine<https://www.Movable-Type.co.UK/scripts/latlong.html>}
1031
- formula.
974
+ '''Compute the I{angular} distance between two (spherical) points using the
975
+ U{Haversine<https://www.Movable-Type.co.UK/scripts/latlong.html>} formula.
1032
976
 
1033
977
  @arg phi2: End latitude (C{radians}).
1034
978
  @arg phi1: Start latitude (C{radians}).
@@ -1036,18 +980,13 @@ def haversine_(phi2, phi1, lam21):
1036
980
 
1037
981
  @return: Angular distance (C{radians}).
1038
982
 
1039
- @see: Functions L{haversine}, L{cosineAndoyerLambert_},
1040
- L{cosineForsytheAndoyerLambert_}, L{cosineLaw_},
1041
- L{euclidean_}, L{flatLocal_}/L{hubeny_}, L{flatPolar_},
1042
- L{thomas_} and L{vincentys_}.
983
+ @see: Functions L{haversine}, L{cosineLaw_}, L{euclidean_}, L{flatLocal_} /
984
+ L{hubeny_}, L{flatPolar_}, L{thomas_} and L{vincentys_}.
1043
985
 
1044
986
  @note: See note at function L{vincentys_}.
1045
987
  '''
1046
- def _hsin(rad):
1047
- return sin(rad * _0_5)**2
1048
-
1049
- h = _hsin(phi2 - phi1) + cos(phi1) * cos(phi2) * _hsin(lam21) # haversine
1050
- return atan2(sqrt0(h), sqrt0(_1_0 - h)) * _2_0 # == asin(sqrt(h)) * 2
988
+ h = hav(phi2 - phi1) + cos(phi1) * cos(phi2) * hav(lam21) # haversine
989
+ return atan2(sqrt0(h), sqrt0(_1_0 - h)) * _2_0 # == asin1(sqrt(h)) * 2
1051
990
 
1052
991
 
1053
992
  def heightOf(angle, distance, radius=R_M):
@@ -1084,22 +1023,23 @@ def heightOf(angle, distance, radius=R_M):
1084
1023
  raise _ValueError(angle=angle, distance=distance, radius=radius)
1085
1024
 
1086
1025
 
1087
- def heightOrthometric(h_ll, N):
1026
+ def heightOrthometric(h_loc, N):
1088
1027
  '''Get the I{orthometric} height B{H}, the height above the geoid, earth surface.
1089
1028
 
1090
- @arg h_ll: The height above the ellipsoid (C{meter}) or an I{ellipsoidal}
1091
- location (C{LatLon} with a C{height} or C{h} attribute).
1029
+ @arg h_loc: The height above the ellipsoid (C{meter}) or an I{ellipsoidal}
1030
+ location (C{LatLon} or C{Cartesian} with a C{height} or C{h}
1031
+ attribute), otherwise C{0 meter}.
1092
1032
  @arg N: The I{geoid} height (C{meter}), the height of the geoid above the
1093
- ellipsoid at the same B{C{h_ll}} location.
1033
+ ellipsoid at the same B{C{h_loc}} location.
1094
1034
 
1095
1035
  @return: I{Orthometric} height C{B{H} = B{h} - B{N}} (C{meter}, same units
1096
1036
  as B{C{h}} and B{C{N}}).
1097
1037
 
1098
- @see: U{Ellipsoid, Geoid, and Othometric Heights<https://www.NGS.NOAA.gov/
1038
+ @see: U{Ellipsoid, Geoid, and Orthometric Heights<https://www.NGS.NOAA.gov/
1099
1039
  GEOID/PRESENTATIONS/2007_02_24_CCPS/Roman_A_PLSC2007notes.pdf>}, page
1100
1040
  6 and module L{pygeodesy.geoids}.
1101
1041
  '''
1102
- h = h_ll if _isHeight(h_ll) else _xattr(h_ll, height=_xattr(h_ll, h=0))
1042
+ h = h_loc if _isHeight(h_loc) else _xattr(h_loc, height=_xattr(h_loc, h=0))
1103
1043
  return Height(H=Height(h=h) - Height(N=N))
1104
1044
 
1105
1045
 
@@ -1121,9 +1061,11 @@ def horizon(height, radius=R_M, refraction=False):
1121
1061
  if min(h, r) < 0:
1122
1062
  raise _ValueError(height=height, radius=radius)
1123
1063
 
1124
- d2 = ((r * 2.415750694528) if refraction else # 2.0 / 0.8279
1125
- fsumf_(r, r, h)) * h
1126
- return sqrt0(d2)
1064
+ if refraction:
1065
+ r *= 2.415750694528 # 2.0 / 0.8279
1066
+ else:
1067
+ r += r + h
1068
+ return sqrt0(r * h)
1127
1069
 
1128
1070
 
1129
1071
  class _idllmn6(object): # see also .geodesicw._wargs, .latlonBase._toCartesian3, .vector2d._numpy
@@ -1170,8 +1112,8 @@ _idllmn6 = _idllmn6() # PYCHOK singleton
1170
1112
 
1171
1113
  def intersection2(lat1, lon1, bearing1,
1172
1114
  lat2, lon2, bearing2, datum=None, wrap=False, small=_100km): # was=True
1173
- '''I{Conveniently} compute the intersection of two lines each defined
1174
- by a (geodetic) point and a bearing from North, using either ...
1115
+ '''I{Conveniently} compute the intersection of two lines each defined by
1116
+ a (geodetic) point and a bearing from North, using either ...
1175
1117
 
1176
1118
  1) L{vector3d.intersection3d3} for B{C{small}} distances (below 100 Km
1177
1119
  or about 0.88 degrees) or if I{no} B{C{datum}} is specified, or ...
@@ -1191,33 +1133,29 @@ def intersection2(lat1, lon1, bearing1,
1191
1133
 
1192
1134
  @arg lat1: Latitude of the first point (C{degrees}).
1193
1135
  @arg lon1: Longitude of the first point (C{degrees}).
1194
- @arg bearing1: Bearing at the first point (compass C{degrees}).
1136
+ @arg bearing1: Bearing at the first point (compass C{degrees360}).
1195
1137
  @arg lat2: Latitude of the second point (C{degrees}).
1196
1138
  @arg lon2: Longitude of the second point (C{degrees}).
1197
- @arg bearing2: Bearing at the second point (compass C{degrees}).
1139
+ @arg bearing2: Bearing at the second point (compass C{degrees360}).
1198
1140
  @kwarg datum: Optional datum (L{Datum}) or ellipsoid (L{Ellipsoid},
1199
- L{Ellipsoid2} or L{a_f2Tuple}) or C{scalar} earth
1200
- radius (C{meter}, same units as B{C{radius1}} and
1201
- B{C{radius2}}) or C{None}.
1202
- @kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{lat2}}
1203
- and B{C{lon2}} (C{bool}).
1141
+ L{Ellipsoid2} or L{a_f2Tuple}) or C{scalar} earth radius
1142
+ (C{meter}, same units as B{C{radius1}} and B{C{radius2}})
1143
+ or C{None}.
1144
+ @kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{lat2}} and
1145
+ B{C{lon2}} (C{bool}).
1204
1146
  @kwarg small: Upper limit for small distances (C{meter}).
1205
1147
 
1206
- @return: A L{LatLon2Tuple}C{(lat, lon)} with the lat- and
1207
- longitude of the intersection point.
1148
+ @return: Intersection point (L{LatLon2Tuple}C{(lat, lon)}).
1208
1149
 
1209
- @raise IntersectionError: Ambiguous or infinite intersection
1210
- or colinear, parallel or otherwise
1211
- non-intersecting lines.
1150
+ @raise IntersectionError: No or an ambiguous intersection or colinear,
1151
+ parallel or otherwise non-intersecting lines.
1212
1152
 
1213
1153
  @raise TypeError: Invalid B{C{datum}}.
1214
1154
 
1215
- @raise UnitError: Invalid B{C{lat1}}, B{C{lon1}}, B{C{bearing1}},
1216
- B{C{lat2}}, B{C{lon2}} or B{C{bearing2}}.
1155
+ @raise UnitError: Invalid B{C{lat1}}, B{C{lon1}}, B{C{bearing1}}, B{C{lat2}},
1156
+ B{C{lon2}} or B{C{bearing2}}.
1217
1157
 
1218
1158
  @see: Method L{RhumbLine.intersection2}.
1219
-
1220
- @note: The returned intersections may be near-antipodal.
1221
1159
  '''
1222
1160
  b1 = Bearing(bearing1=bearing1)
1223
1161
  b2 = Bearing(bearing2=bearing2)
@@ -1263,26 +1201,24 @@ def intersections2(lat1, lon1, radius1,
1263
1201
  @arg lat2: Latitude of the second circle center (C{degrees}).
1264
1202
  @arg lon2: Longitude of the second circle center (C{degrees}).
1265
1203
  @arg radius2: Radius of the second circle (C{meter}, same units as B{C{radius1}}).
1266
- @kwarg datum: Optional datum (L{Datum}) or ellipsoid (L{Ellipsoid},
1267
- L{Ellipsoid2} or L{a_f2Tuple}) or C{scalar} earth
1268
- radius (C{meter}, same units as B{C{radius1}} and
1269
- B{C{radius2}}) or C{None}.
1270
- @kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{lat2}}
1271
- and B{C{lon2}} (C{bool}).
1204
+ @kwarg datum: Optional datum (L{Datum}) or ellipsoid (L{Ellipsoid}, L{Ellipsoid2}
1205
+ or L{a_f2Tuple}) or C{scalar} earth radius (C{meter}, same units as
1206
+ B{C{radius1}} and B{C{radius2}}) or C{None}.
1207
+ @kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{lat2}} and B{C{lon2}}
1208
+ (C{bool}).
1272
1209
  @kwarg small: Upper limit for small distances (C{meter}).
1273
1210
 
1274
- @return: 2-Tuple of the intersection points, each a
1275
- L{LatLon2Tuple}C{(lat, lon)}. For abutting circles, the
1276
- points are the same instance, aka the I{radical center}.
1211
+ @return: 2-Tuple of the intersection points, each a L{LatLon2Tuple}C{(lat, lon)}.
1212
+ Both points are the same instance, aka the I{radical center} if the
1213
+ circles are abutting
1277
1214
 
1278
- @raise IntersectionError: Concentric, antipodal, invalid or
1279
- non-intersecting circles or no
1280
- convergence.
1215
+ @raise IntersectionError: Concentric, antipodal, invalid or non-intersecting
1216
+ circles or no convergence.
1281
1217
 
1282
1218
  @raise TypeError: Invalid B{C{datum}}.
1283
1219
 
1284
- @raise UnitError: Invalid B{C{lat1}}, B{C{lon1}}, B{C{radius1}},
1285
- B{C{lat2}}, B{C{lon2}} or B{C{radius2}}.
1220
+ @raise UnitError: Invalid B{C{lat1}}, B{C{lon1}}, B{C{radius1}}, B{C{lat2}},
1221
+ B{C{lon2}} or B{C{radius2}}.
1286
1222
  '''
1287
1223
  r1 = Radius_(radius1=radius1)
1288
1224
  r2 = Radius_(radius2=radius2)
@@ -1356,7 +1292,7 @@ def _isequalTo(p1, p2, eps=EPS):
1356
1292
  fabs(p1.lon - p2.lon) <= eps) if eps else (p1.latlon == p2.latlon)
1357
1293
 
1358
1294
 
1359
- def _isequalTo_(p1, p2, eps=EPS):
1295
+ def _isequalTo_(p1, p2, eps=EPS): # underscore_!
1360
1296
  '''(INTERNAL) Compare 2 point phi-/lams ignoring C{class}.
1361
1297
  '''
1362
1298
  return (fabs(p1.phi - p2.phi) <= eps and
@@ -1372,11 +1308,11 @@ def isnormal(lat, lon, eps=0):
1372
1308
  @kwarg eps: Optional tolerance C{degrees}).
1373
1309
 
1374
1310
  @return: C{True} if C{(abs(B{lat}) + B{eps}) <= 90} and
1375
- C{(abs(B{lon}) + B{eps}) <= 180}, C{False} othwerwise.
1311
+ C{(abs(B{lon}) + B{eps}) <= 180}, C{False} otherwise.
1376
1312
 
1377
1313
  @see: Functions L{isnormal_} and L{normal}.
1378
1314
  '''
1379
- return (_90_0 - fabs(lat)) >= eps and _loneg(fabs(lon)) >= eps
1315
+ return _loneg(fabs(lon)) >= eps and (_90_0 - fabs(lat)) >= eps # co-latitude
1380
1316
 
1381
1317
 
1382
1318
  def isnormal_(phi, lam, eps=0):
@@ -1395,22 +1331,10 @@ def isnormal_(phi, lam, eps=0):
1395
1331
  return (PI_2 - fabs(phi)) >= eps and (PI - fabs(lam)) >= eps
1396
1332
 
1397
1333
 
1398
- def latlon2n_xyz(lat, lon, **name):
1399
- '''Convert lat-, longitude to C{n-vector} (I{normal} to the
1400
- earth's surface) X, Y and Z components.
1401
-
1402
- @arg lat: Latitude (C{degrees}).
1403
- @arg lon: Longitude (C{degrees}).
1404
- @kwarg name: Optional C{B{name}=NN} (C{str}).
1405
-
1406
- @return: A L{Vector3Tuple}C{(x, y, z)}.
1407
-
1408
- @see: Function L{philam2n_xyz}.
1409
-
1410
- @note: These are C{n-vector} x, y and z components,
1411
- I{NOT} geocentric ECEF x, y and z coordinates!
1334
+ def _maprod(fun_, *ts):
1335
+ '''(INTERNAL) Helper for C{excessCagnoli_} and C{excessLHuilier_}.
1412
1336
  '''
1413
- return _2n_xyz(name, *sincos2d_(lat, lon))
1337
+ return fprod(map(fun_, ts))
1414
1338
 
1415
1339
 
1416
1340
  def _normal2(a, b, n_2, n, n2):
@@ -1431,10 +1355,10 @@ def normal(lat, lon, **name):
1431
1355
 
1432
1356
  @arg lat: Latitude (C{degrees}).
1433
1357
  @arg lon: Longitude (C{degrees}).
1434
- @kwarg name: Optional, overriding C{B{name}="normal"} (C{str}).
1358
+ @kwarg name: Optional C{B{name}="normal"} (C{str}).
1435
1359
 
1436
- @return: L{LatLon2Tuple}C{(lat, lon)} with C{abs(lat) <= 90}
1437
- and C{abs(lon) <= 180}.
1360
+ @return: L{LatLon2Tuple}C{(lat, lon)} with C{-90 <= lat <= 90}
1361
+ and C{-180 <= lon <= 180}.
1438
1362
 
1439
1363
  @see: Functions L{normal_} and L{isnormal}.
1440
1364
  '''
@@ -1447,7 +1371,7 @@ def normal_(phi, lam, **name):
1447
1371
 
1448
1372
  @arg phi: Latitude (C{radians}).
1449
1373
  @arg lam: Longitude (C{radians}).
1450
- @kwarg name: Optional, overriding C{B{name}="normal_"} (C{str}).
1374
+ @kwarg name: Optional C{B{name}="normal_"} (C{str}).
1451
1375
 
1452
1376
  @return: L{PhiLam2Tuple}C{(phi, lam)} with C{abs(phi) <= PI/2}
1453
1377
  and C{abs(lam) <= PI}.
@@ -1458,44 +1382,6 @@ def normal_(phi, lam, **name):
1458
1382
  name=_name__(name, name__=normal_))
1459
1383
 
1460
1384
 
1461
- def _2n_xyz(name, sa, ca, sb, cb): # name always **name
1462
- '''(INTERNAL) Helper for C{latlon2n_xyz} and C{philam2n_xyz}.
1463
- '''
1464
- # Kenneth Gade eqn 3, but using right-handed
1465
- # vector x -> 0°E,0°N, y -> 90°E,0°N, z -> 90°N
1466
- return Vector3Tuple(ca * cb, ca * sb, sa, **name)
1467
-
1468
-
1469
- def n_xyz2latlon(x, y, z, **name):
1470
- '''Convert C{n-vector} components to lat- and longitude in C{degrees}.
1471
-
1472
- @arg x: X component (C{scalar}).
1473
- @arg y: Y component (C{scalar}).
1474
- @arg z: Z component (C{scalar}).
1475
- @kwarg name: Optional C{B{name}=NN} (C{str}).
1476
-
1477
- @return: A L{LatLon2Tuple}C{(lat, lon)}.
1478
-
1479
- @see: Function L{n_xyz2philam}.
1480
- '''
1481
- return LatLon2Tuple(atan2d(z, hypot(x, y)), atan2d(y, x), **name)
1482
-
1483
-
1484
- def n_xyz2philam(x, y, z, **name):
1485
- '''Convert C{n-vector} components to lat- and longitude in C{radians}.
1486
-
1487
- @arg x: X component (C{scalar}).
1488
- @arg y: Y component (C{scalar}).
1489
- @arg z: Z component (C{scalar}).
1490
- @kwarg name: Optional C{B{name}=NN} (C{str}).
1491
-
1492
- @return: A L{PhiLam2Tuple}C{(phi, lam)}.
1493
-
1494
- @see: Function L{n_xyz2latlon}.
1495
- '''
1496
- return PhiLam2Tuple(atan2(z, hypot(x, y)), atan2(y, x), **name)
1497
-
1498
-
1499
1385
  def _opposes(d, m, n, n2):
1500
1386
  '''(INTERNAL) Helper for C{opposing} and C{opposing_}.
1501
1387
  '''
@@ -1536,24 +1422,6 @@ def opposing_(radians1, radians2, margin=PI_2):
1536
1422
  return _opposes(radians2 - radians1, m, PI, PI2)
1537
1423
 
1538
1424
 
1539
- def philam2n_xyz(phi, lam, **name):
1540
- '''Convert lat-, longitude to C{n-vector} (I{normal} to the
1541
- earth's surface) X, Y and Z components.
1542
-
1543
- @arg phi: Latitude (C{radians}).
1544
- @arg lam: Longitude (C{radians}).
1545
- @kwarg name: Optional name (C{str}).
1546
-
1547
- @return: A L{Vector3Tuple}C{(x, y, z)}.
1548
-
1549
- @see: Function L{latlon2n_xyz}.
1550
-
1551
- @note: These are C{n-vector} x, y and z components,
1552
- I{NOT} geocentric ECEF x, y and z coordinates!
1553
- '''
1554
- return _2n_xyz(name, *sincos2_(phi, lam))
1555
-
1556
-
1557
1425
  def _Propy(func, nargs, kwds):
1558
1426
  '''(INTERNAL) Helper for the C{frechet.[-]Frechet**} and
1559
1427
  C{hausdorff.[-]Hausdorff*} classes.
@@ -1577,29 +1445,24 @@ def _radical2(d, r1, r2, **name): # in .ellipsoidalBaseDI, .sphericalTrigonomet
1577
1445
 
1578
1446
 
1579
1447
  def radical2(distance, radius1, radius2, **name):
1580
- '''Compute the I{radical ratio} and I{radical line} of two
1581
- U{intersecting circles<https://MathWorld.Wolfram.com/
1582
- Circle-CircleIntersection.html>}.
1448
+ '''Compute the I{radical ratio} and I{radical line} of two U{intersecting
1449
+ circles<https://MathWorld.Wolfram.com/Circle-CircleIntersection.html>}.
1583
1450
 
1584
- The I{radical line} is perpendicular to the axis thru the
1585
- centers of the circles at C{(0, 0)} and C{(B{distance}, 0)}.
1451
+ The I{radical line} is perpendicular to the axis thru the centers of
1452
+ the circles at C{(0, 0)} and C{(B{distance}, 0)}.
1586
1453
 
1587
1454
  @arg distance: Distance between the circle centers (C{scalar}).
1588
1455
  @arg radius1: Radius of the first circle (C{scalar}).
1589
1456
  @arg radius2: Radius of the second circle (C{scalar}).
1590
1457
  @kwarg name: Optional C{B{name}=NN} (C{str}).
1591
1458
 
1592
- @return: A L{Radical2Tuple}C{(ratio, xline)} where C{0.0 <=
1593
- ratio <= 1.0} and C{xline} is along the B{C{distance}}.
1459
+ @return: A L{Radical2Tuple}C{(ratio, xline)} where C{0.0 <= ratio <=
1460
+ 1.0} and C{xline} is along the B{C{distance}}.
1594
1461
 
1595
- @raise IntersectionError: The B{C{distance}} exceeds the sum
1596
- of B{C{radius1}} and B{C{radius2}}.
1462
+ @raise IntersectionError: The B{C{distance}} exceeds the sum of
1463
+ B{C{radius1}} and B{C{radius2}}.
1597
1464
 
1598
- @raise UnitError: Invalid B{C{distance}}, B{C{radius1}} or
1599
- B{C{radius2}}.
1600
-
1601
- @see: U{Circle-Circle Intersection
1602
- <https://MathWorld.Wolfram.com/Circle-CircleIntersection.html>}.
1465
+ @raise UnitError: Invalid B{C{distance}}, B{C{radius1}} or B{C{radius2}}.
1603
1466
  '''
1604
1467
  d = Distance_(distance, low=_0_0)
1605
1468
  r1 = Radius_(radius1=radius1)
@@ -1649,7 +1512,7 @@ def _radistance(inst):
1649
1512
  def _scale_deg(lat1, lat2): # degrees
1650
1513
  # scale factor cos(mean of lats) for delta lon
1651
1514
  m = fabs(lat1 + lat2) * _0_5
1652
- return cos(radians(m)) if m < 90 else _0_0
1515
+ return cos(radians(m)) if m < _90_0 else _0_0
1653
1516
 
1654
1517
 
1655
1518
  def _scale_rad(phi1, phi2): # radians, by .frechet, .hausdorff, .heights
@@ -1666,57 +1529,53 @@ def _sincosa6(phi2, phi1, lam21): # [4] in cosineLaw
1666
1529
 
1667
1530
 
1668
1531
  def thomas(lat1, lon1, lat2, lon2, datum=_WGS84, wrap=False):
1669
- '''Compute the distance between two (ellipsoidal) points using
1670
- U{Thomas'<https://apps.DTIC.mil/dtic/tr/fulltext/u2/703541.pdf>}
1671
- formula.
1532
+ '''Compute the distance between two (ellipsoidal) points using U{Thomas'
1533
+ <https://apps.DTIC.mil/dtic/tr/fulltext/u2/703541.pdf>} formula.
1672
1534
 
1673
1535
  @arg lat1: Start latitude (C{degrees}).
1674
1536
  @arg lon1: Start longitude (C{degrees}).
1675
1537
  @arg lat2: End latitude (C{degrees}).
1676
1538
  @arg lon2: End longitude (C{degrees}).
1677
- @kwarg datum: Datum (L{Datum}) or ellipsoid (L{Ellipsoid},
1678
- L{Ellipsoid2} or L{a_f2Tuple}) to use.
1679
- @kwarg wrap: If C{True}, wrap or I{normalize} and unroll
1680
- B{C{lat2}} and B{C{lon2}} (C{bool}).
1539
+ @kwarg datum: Datum (L{Datum}) or ellipsoid (L{Ellipsoid}, L{Ellipsoid2}
1540
+ or L{a_f2Tuple}) to use.
1541
+ @kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{lat2}} and
1542
+ B{C{lon2}} (C{bool}).
1681
1543
 
1682
- @return: Distance (C{meter}, same units as the B{C{datum}}'s
1683
- ellipsoid axes).
1544
+ @return: Distance (C{meter}, same units as the B{C{datum}}'s or ellipsoid axes).
1684
1545
 
1685
1546
  @raise TypeError: Invalid B{C{datum}}.
1686
1547
 
1687
- @see: Functions L{thomas_}, L{cosineAndoyerLambert}, L{cosineForsytheAndoyerLambert},
1688
- L{cosineLaw}, L{equirectangular}, L{euclidean}, L{flatLocal}/L{hubeny},
1689
- L{flatPolar}, L{haversine}, L{vincentys} and method L{Ellipsoid.distance2}.
1548
+ @see: Functions L{thomas_}, L{cosineLaw}, L{equirectangular}, L{euclidean},
1549
+ L{flatLocal} / L{hubeny}, L{flatPolar}, L{haversine}, L{vincentys} and
1550
+ method L{Ellipsoid.distance2}.
1690
1551
  '''
1691
1552
  return _dE(thomas_, datum, wrap, lat1, lon1, lat2, lon2)
1692
1553
 
1693
1554
 
1694
1555
  def thomas_(phi2, phi1, lam21, datum=_WGS84):
1695
1556
  '''Compute the I{angular} distance between two (ellipsoidal) points using
1696
- U{Thomas'<https://apps.DTIC.mil/dtic/tr/fulltext/u2/703541.pdf>}
1697
- formula.
1557
+ U{Thomas'<https://apps.DTIC.mil/dtic/tr/fulltext/u2/703541.pdf>} formula.
1698
1558
 
1699
1559
  @arg phi2: End latitude (C{radians}).
1700
1560
  @arg phi1: Start latitude (C{radians}).
1701
1561
  @arg lam21: Longitudinal delta, M{end-start} (C{radians}).
1702
- @kwarg datum: Datum or ellipsoid to use (L{Datum}, L{Ellipsoid},
1562
+ @kwarg datum: Datum (L{Datum}) ?or ellipsoid to use (L{Ellipsoid},
1703
1563
  L{Ellipsoid2} or L{a_f2Tuple}).
1704
1564
 
1705
1565
  @return: Angular distance (C{radians}).
1706
1566
 
1707
1567
  @raise TypeError: Invalid B{C{datum}}.
1708
1568
 
1709
- @see: Functions L{thomas}, L{cosineAndoyerLambert_},
1710
- L{cosineForsytheAndoyerLambert_}, L{cosineLaw_},
1711
- L{euclidean_}, L{flatLocal_}/L{hubeny_}, L{flatPolar_},
1712
- L{haversine_} and L{vincentys_} and U{Geodesy-PHP
1713
- <https://GitHub.com/jtejido/geodesy-php/blob/master/src/Geodesy/
1714
- Distance/ThomasFormula.php>}.
1569
+ @see: Functions L{thomas}, L{cosineLaw_}, L{euclidean_}, L{flatLocal_} /
1570
+ L{hubeny_}, L{flatPolar_}, L{haversine_} and L{vincentys_} and
1571
+ U{Geodesy-PHP<https://GitHub.com/jtejido/geodesy-php/blob/master/
1572
+ src/Geodesy/Distance/ThomasFormula.php>}.
1715
1573
  '''
1716
1574
  s2, c2, s1, c1, r, _ = _sincosa6(phi2, phi1, lam21)
1717
1575
  if r and isnon0(c1) and isnon0(c2):
1718
1576
  E = _ellipsoidal(datum, thomas_)
1719
- if E.f:
1577
+ f = E.f * _0_25
1578
+ if f: # ellipsoidal
1720
1579
  r1 = atan2(E.b_a * s1, c1)
1721
1580
  r2 = atan2(E.b_a * s2, c2)
1722
1581
 
@@ -1730,21 +1589,20 @@ def thomas_(phi2, phi1, lam21, datum=_WGS84):
1730
1589
  r = atan(sqrt0(h / u)) * 2 # == acos(1 - 2 * h)
1731
1590
  sr, cr = sincos2(r)
1732
1591
  if isnon0(sr):
1733
- u = 2 * (sj * ck)**2 / u
1734
- h = 2 * (sk * cj)**2 / h
1592
+ u = (sj * ck)**2 * 2 / u
1593
+ h = (sk * cj)**2 * 2 / h
1735
1594
  x = u + h
1736
1595
  y = u - h
1737
1596
 
1597
+ b = r * 2
1738
1598
  s = r / sr
1739
1599
  e = 4 * s**2
1740
1600
  d = 2 * cr
1741
1601
  a = e * d
1742
- b = 2 * r
1743
1602
  c = s - (a - d) * _0_5
1744
- f = E.f * _0_25
1745
1603
 
1746
- t = fsumf_(a * x, -b * y, c * x**2, -d * y**2, e * x * y)
1747
- r -= fsumf_(s * x, -y, -t * f * _0_25) * f * sr
1604
+ t = fdot_(a, x, -b, y, -d, y**2, c, x**2, e, x * y) * _0_25
1605
+ r -= fdot_(s, x, -1, y, -t, f) * f * sr
1748
1606
  return r
1749
1607
 
1750
1608
 
@@ -1757,21 +1615,20 @@ def vincentys(lat1, lon1, lat2, lon2, radius=R_M, wrap=False):
1757
1615
  @arg lon1: Start longitude (C{degrees}).
1758
1616
  @arg lat2: End latitude (C{degrees}).
1759
1617
  @arg lon2: End longitude (C{degrees}).
1760
- @kwarg radius: Mean earth radius (C{meter}), datum (L{Datum})
1761
- or ellipsoid (L{Ellipsoid}, L{Ellipsoid2} or
1762
- L{a_f2Tuple}) to use.
1763
- @kwarg wrap: If C{True}, wrap or I{normalize} and unroll
1764
- B{C{lat2}} and B{C{lon2}} (C{bool}).
1618
+ @kwarg radius: Mean earth radius (C{meter}), datum (L{Datum}) or
1619
+ ellipsoid (L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple})
1620
+ to use.
1621
+ @kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{lat2}} and
1622
+ B{C{lon2}} (C{bool}).
1765
1623
 
1766
1624
  @return: Distance (C{meter}, same units as B{C{radius}}).
1767
1625
 
1768
1626
  @raise UnitError: Invalid B{C{radius}}.
1769
1627
 
1770
- @see: Functions L{vincentys_}, L{cosineAndoyerLambert},
1771
- L{cosineForsytheAndoyerLambert},L{cosineLaw}, L{equirectangular},
1772
- L{euclidean}, L{flatLocal}/L{hubeny}, L{flatPolar},
1773
- L{haversine} and L{thomas} and methods L{Ellipsoid.distance2},
1774
- C{LatLon.distanceTo*} and C{LatLon.equirectangularTo}.
1628
+ @see: Functions L{vincentys_}, L{cosineLaw}, L{equirectangular}, L{euclidean},
1629
+ L{flatLocal}/L{hubeny}, L{flatPolar}, L{haversine} and L{thomas} and
1630
+ methods L{Ellipsoid.distance2}, C{LatLon.distanceTo*} and
1631
+ C{LatLon.equirectangularTo}.
1775
1632
 
1776
1633
  @note: See note at function L{vincentys_}.
1777
1634
  '''
@@ -1789,17 +1646,14 @@ def vincentys_(phi2, phi1, lam21):
1789
1646
 
1790
1647
  @return: Angular distance (C{radians}).
1791
1648
 
1792
- @see: Functions L{vincentys}, L{cosineAndoyerLambert_},
1793
- L{cosineForsytheAndoyerLambert_}, L{cosineLaw_},
1794
- L{euclidean_}, L{flatLocal_}/L{hubeny_}, L{flatPolar_},
1795
- L{haversine_} and L{thomas_}.
1649
+ @see: Functions L{vincentys}, L{cosineLaw_}, L{euclidean_}, L{flatLocal_} /
1650
+ L{hubeny_}, L{flatPolar_}, L{haversine_} and L{thomas_}.
1796
1651
 
1797
- @note: Functions L{vincentys_}, L{haversine_} and L{cosineLaw_}
1798
- produce equivalent results, but L{vincentys_} is suitable
1799
- for antipodal points and slightly more expensive (M{3 cos,
1800
- 3 sin, 1 hypot, 1 atan2, 6 mul, 2 add}) than L{haversine_}
1801
- (M{2 cos, 2 sin, 2 sqrt, 1 atan2, 5 mul, 1 add}) and
1802
- L{cosineLaw_} (M{3 cos, 3 sin, 1 acos, 3 mul, 1 add}).
1652
+ @note: Functions L{vincentys_}, L{haversine_} and L{cosineLaw_} produce
1653
+ equivalent results, but L{vincentys_} is suitable for antipodal
1654
+ points and slightly more expensive (M{3 cos, 3 sin, 1 hypot, 1 atan2,
1655
+ 6 mul, 2 add}) than L{haversine_} (M{2 cos, 2 sin, 2 sqrt, 1 atan2, 5
1656
+ mul, 1 add}) and L{cosineLaw_} (M{3 cos, 3 sin, 1 acos, 3 mul, 1 add}).
1803
1657
  '''
1804
1658
  s1, c1, s2, c2, s21, c21 = sincos2_(phi1, phi2, lam21)
1805
1659
 
@@ -1810,7 +1664,7 @@ def vincentys_(phi2, phi1, lam21):
1810
1664
 
1811
1665
  # **) MIT License
1812
1666
  #
1813
- # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
1667
+ # Copyright (C) 2016-2025 -- mrJean1 at Gmail -- All Rights Reserved.
1814
1668
  #
1815
1669
  # Permission is hereby granted, free of charge, to any person obtaining a
1816
1670
  # copy of this software and associated documentation files (the "Software"),