pygeodesy 24.3.24__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- PyGeodesy-24.3.24.dist-info/METADATA +272 -0
- PyGeodesy-24.3.24.dist-info/RECORD +115 -0
- PyGeodesy-24.3.24.dist-info/WHEEL +6 -0
- PyGeodesy-24.3.24.dist-info/top_level.txt +1 -0
- pygeodesy/LICENSE +21 -0
- pygeodesy/__init__.py +615 -0
- pygeodesy/__main__.py +103 -0
- pygeodesy/albers.py +867 -0
- pygeodesy/auxilats/_CX_4.py +218 -0
- pygeodesy/auxilats/_CX_6.py +314 -0
- pygeodesy/auxilats/_CX_8.py +475 -0
- pygeodesy/auxilats/__init__.py +54 -0
- pygeodesy/auxilats/__main__.py +86 -0
- pygeodesy/auxilats/auxAngle.py +548 -0
- pygeodesy/auxilats/auxDLat.py +302 -0
- pygeodesy/auxilats/auxDST.py +296 -0
- pygeodesy/auxilats/auxLat.py +848 -0
- pygeodesy/auxilats/auxily.py +272 -0
- pygeodesy/azimuthal.py +1150 -0
- pygeodesy/basics.py +892 -0
- pygeodesy/booleans.py +2031 -0
- pygeodesy/cartesianBase.py +1062 -0
- pygeodesy/clipy.py +704 -0
- pygeodesy/constants.py +516 -0
- pygeodesy/css.py +660 -0
- pygeodesy/datums.py +752 -0
- pygeodesy/deprecated/__init__.py +61 -0
- pygeodesy/deprecated/bases.py +40 -0
- pygeodesy/deprecated/classes.py +262 -0
- pygeodesy/deprecated/consterns.py +54 -0
- pygeodesy/deprecated/datum.py +40 -0
- pygeodesy/deprecated/functions.py +375 -0
- pygeodesy/deprecated/nvector.py +48 -0
- pygeodesy/deprecated/rhumbBase.py +32 -0
- pygeodesy/deprecated/rhumbaux.py +33 -0
- pygeodesy/deprecated/rhumbsolve.py +33 -0
- pygeodesy/deprecated/rhumbx.py +33 -0
- pygeodesy/dms.py +986 -0
- pygeodesy/ecef.py +1348 -0
- pygeodesy/elevations.py +279 -0
- pygeodesy/ellipsoidalBase.py +1224 -0
- pygeodesy/ellipsoidalBaseDI.py +913 -0
- pygeodesy/ellipsoidalExact.py +343 -0
- pygeodesy/ellipsoidalGeodSolve.py +343 -0
- pygeodesy/ellipsoidalKarney.py +403 -0
- pygeodesy/ellipsoidalNvector.py +685 -0
- pygeodesy/ellipsoidalVincenty.py +590 -0
- pygeodesy/ellipsoids.py +2476 -0
- pygeodesy/elliptic.py +1198 -0
- pygeodesy/epsg.py +243 -0
- pygeodesy/errors.py +804 -0
- pygeodesy/etm.py +1190 -0
- pygeodesy/fmath.py +1013 -0
- pygeodesy/formy.py +1818 -0
- pygeodesy/frechet.py +865 -0
- pygeodesy/fstats.py +760 -0
- pygeodesy/fsums.py +1898 -0
- pygeodesy/gars.py +358 -0
- pygeodesy/geodesicw.py +581 -0
- pygeodesy/geodesicx/_C4_24.py +1699 -0
- pygeodesy/geodesicx/_C4_27.py +2395 -0
- pygeodesy/geodesicx/_C4_30.py +3301 -0
- pygeodesy/geodesicx/__init__.py +48 -0
- pygeodesy/geodesicx/__main__.py +91 -0
- pygeodesy/geodesicx/gx.py +1382 -0
- pygeodesy/geodesicx/gxarea.py +535 -0
- pygeodesy/geodesicx/gxbases.py +154 -0
- pygeodesy/geodesicx/gxline.py +669 -0
- pygeodesy/geodsolve.py +426 -0
- pygeodesy/geohash.py +914 -0
- pygeodesy/geoids.py +1884 -0
- pygeodesy/hausdorff.py +892 -0
- pygeodesy/heights.py +1155 -0
- pygeodesy/interns.py +687 -0
- pygeodesy/iters.py +545 -0
- pygeodesy/karney.py +919 -0
- pygeodesy/ktm.py +633 -0
- pygeodesy/latlonBase.py +1766 -0
- pygeodesy/lazily.py +960 -0
- pygeodesy/lcc.py +684 -0
- pygeodesy/ltp.py +1107 -0
- pygeodesy/ltpTuples.py +1563 -0
- pygeodesy/mgrs.py +721 -0
- pygeodesy/named.py +1324 -0
- pygeodesy/namedTuples.py +683 -0
- pygeodesy/nvectorBase.py +695 -0
- pygeodesy/osgr.py +781 -0
- pygeodesy/points.py +1686 -0
- pygeodesy/props.py +628 -0
- pygeodesy/resections.py +1048 -0
- pygeodesy/rhumb/__init__.py +46 -0
- pygeodesy/rhumb/aux_.py +397 -0
- pygeodesy/rhumb/bases.py +1148 -0
- pygeodesy/rhumb/ekx.py +563 -0
- pygeodesy/rhumb/solve.py +572 -0
- pygeodesy/simplify.py +647 -0
- pygeodesy/solveBase.py +472 -0
- pygeodesy/sphericalBase.py +724 -0
- pygeodesy/sphericalNvector.py +1264 -0
- pygeodesy/sphericalTrigonometry.py +1447 -0
- pygeodesy/streprs.py +627 -0
- pygeodesy/trf.py +2079 -0
- pygeodesy/triaxials.py +1484 -0
- pygeodesy/units.py +969 -0
- pygeodesy/unitsBase.py +349 -0
- pygeodesy/ups.py +538 -0
- pygeodesy/utily.py +1231 -0
- pygeodesy/utm.py +762 -0
- pygeodesy/utmups.py +318 -0
- pygeodesy/utmupsBase.py +517 -0
- pygeodesy/vector2d.py +785 -0
- pygeodesy/vector3d.py +968 -0
- pygeodesy/vector3dBase.py +1049 -0
- pygeodesy/webmercator.py +383 -0
- pygeodesy/wgrs.py +439 -0
|
@@ -0,0 +1,669 @@
|
|
|
1
|
+
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
u'''A pure Python version of I{Karney}'s C++ class U{GeodesicLineExact
|
|
5
|
+
<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1GeodesicLineExact.html>}.
|
|
6
|
+
|
|
7
|
+
Class L{GeodesicLineExact} follows the naming, methods and return
|
|
8
|
+
values from class C{GeodesicLine} from I{Karney}'s Python U{geographiclib
|
|
9
|
+
<https://GeographicLib.SourceForge.io/1.52/python/index.html>}.
|
|
10
|
+
|
|
11
|
+
Copyright (C) U{Charles Karney<mailto:Karney@Alum.MIT.edu>} (2008-2023)
|
|
12
|
+
and licensed under the MIT/X11 License. For more information, see the
|
|
13
|
+
U{GeographicLib<https://GeographicLib.SourceForge.io>} documentation.
|
|
14
|
+
'''
|
|
15
|
+
# make sure int/int division yields float quotient
|
|
16
|
+
from __future__ import division as _; del _ # PYCHOK semicolon
|
|
17
|
+
|
|
18
|
+
# A copy of comments from Karney's C{GeodesicLineExact.cpp}:
|
|
19
|
+
#
|
|
20
|
+
# This is a reformulation of the geodesic problem. The
|
|
21
|
+
# notation is as follows:
|
|
22
|
+
# - at a general point (no suffix or 1 or 2 as suffix)
|
|
23
|
+
# - phi = latitude
|
|
24
|
+
# - lambda = longitude
|
|
25
|
+
# - beta = latitude on auxiliary sphere
|
|
26
|
+
# - omega = longitude on auxiliary sphere
|
|
27
|
+
# - alpha = azimuth of great circle
|
|
28
|
+
# - sigma = arc length along great circle
|
|
29
|
+
# - s = distance
|
|
30
|
+
# - tau = scaled distance (= sigma at multiples of PI/2)
|
|
31
|
+
# - at northwards equator crossing
|
|
32
|
+
# - beta = phi = 0
|
|
33
|
+
# - omega = lambda = 0
|
|
34
|
+
# - alpha = alpha0
|
|
35
|
+
# - sigma = s = 0
|
|
36
|
+
# - a 12 suffix means a difference, e.g., s12 = s2 - s1.
|
|
37
|
+
# - s and c prefixes mean sin and cos
|
|
38
|
+
|
|
39
|
+
# from pygeodesy.basics import _xinstanceof # _MODS
|
|
40
|
+
from pygeodesy.constants import NAN, _EPSmin, _EPSqrt as _TOL, _0_0, \
|
|
41
|
+
_1_0, _180_0, _2__PI, _copysign_1_0
|
|
42
|
+
from pygeodesy.errors import _xError, _xkwds_get
|
|
43
|
+
from pygeodesy.fsums import fsumf_, fsum1f_
|
|
44
|
+
from pygeodesy.geodesicx.gxbases import _cosSeries, _GeodesicBase, \
|
|
45
|
+
_sincos12, _sin1cos2
|
|
46
|
+
# from pygeodesy.geodesicw import _Intersecant2 # _MODS
|
|
47
|
+
from pygeodesy.interns import NN, _COMMASPACE_
|
|
48
|
+
from pygeodesy.lazily import _ALL_DOCS, _ALL_MODS as _MODS
|
|
49
|
+
from pygeodesy.karney import _around, _atan2d, Caps, GDict, _fix90, \
|
|
50
|
+
_K_2_0, _norm2, _norm180, _sincos2, _sincos2d
|
|
51
|
+
from pygeodesy.props import Property_RO, _update_all
|
|
52
|
+
# from pygeodesy.streprs import pairs # _MODS
|
|
53
|
+
from pygeodesy.utily import atan2d as _atan2d_reverse, sincos2
|
|
54
|
+
|
|
55
|
+
from math import atan2, cos, degrees, fabs, floor, radians, sin
|
|
56
|
+
|
|
57
|
+
__all__ = ()
|
|
58
|
+
__version__ = '24.02.21'
|
|
59
|
+
|
|
60
|
+
_glXs = [] # instances of C{[_]GeodesicLineExact} to be updated
|
|
61
|
+
# underflow guard, we require _TINY * EPS > 0, _TINY + EPS == EPS
|
|
62
|
+
_TINY = _EPSmin
|
|
63
|
+
# assert (_TINY * EPS) > 0 and (_TINY + EPS) == EPS
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _update_glXs(gX): # see GeodesicExact.C4order and -._ef_reset_k2
|
|
67
|
+
'''(INTERNAL) Zap cached/memoized C{Property[_RO]}s of
|
|
68
|
+
any L{GeodesicLineExact} instances tied to the given
|
|
69
|
+
L{GeodesicExact} instance B{C{gX}}.
|
|
70
|
+
'''
|
|
71
|
+
_xGeodesicExact(gX=gX)
|
|
72
|
+
for glX in _glXs: # PYCHOK use weakref?
|
|
73
|
+
if glX._gX is gX:
|
|
74
|
+
_update_all(glX)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _xGeodesicExact(**gX):
|
|
78
|
+
'''(INTERNAL) Check a L{GeodesicExact} instance.
|
|
79
|
+
'''
|
|
80
|
+
_MODS.basics._xinstanceof(_MODS.geodesicx.GeodesicExact, **gX)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class _GeodesicLineExact(_GeodesicBase):
|
|
84
|
+
'''(INTERNAL) Base class for L{GeodesicLineExact}.
|
|
85
|
+
'''
|
|
86
|
+
_a13 = _s13 = NAN
|
|
87
|
+
# _azi1 = _0_0
|
|
88
|
+
# _cchi1 = NAN
|
|
89
|
+
# _dn1 = NAN
|
|
90
|
+
_gX = None # Exact only
|
|
91
|
+
# _k2 = NAN
|
|
92
|
+
# _lat1 = _lon1 = _0_0
|
|
93
|
+
# _salp0 = _calp0 = NAN
|
|
94
|
+
# _salp1 = _calp1 = NAN
|
|
95
|
+
# _somg1 = _comg1 = NAN
|
|
96
|
+
# _ssig1 = _csig1 = NAN
|
|
97
|
+
|
|
98
|
+
def __init__(self, gX, lat1, lon1, azi1, caps, _debug, *salp1_calp1, **name): # name=NN
|
|
99
|
+
'''(INTERNAL) New C{[_]GeodesicLineExact} instance.
|
|
100
|
+
'''
|
|
101
|
+
_xGeodesicExact(gX=gX)
|
|
102
|
+
Cs = Caps
|
|
103
|
+
if _debug: # PYCHOK no cover
|
|
104
|
+
self._debug |= _debug & Cs._DEBUG_ALL
|
|
105
|
+
# _CapsBase.debug._update(self)
|
|
106
|
+
if salp1_calp1:
|
|
107
|
+
salp1, calp1 = salp1_calp1
|
|
108
|
+
else:
|
|
109
|
+
azi1 = _norm180(azi1)
|
|
110
|
+
# guard against salp0 underflow,
|
|
111
|
+
# also -0 is converted to +0
|
|
112
|
+
salp1, calp1 = _sincos2d(_around(azi1))
|
|
113
|
+
if name: # *args, name=NN): Python3
|
|
114
|
+
name = _xkwds_get(name, name=NN)
|
|
115
|
+
if name:
|
|
116
|
+
self.name = name
|
|
117
|
+
|
|
118
|
+
self._gX = gX # GeodesicExact only
|
|
119
|
+
self._lat1 = lat1 = _fix90(lat1)
|
|
120
|
+
self._lon1 = lon1
|
|
121
|
+
self._azi1 = azi1
|
|
122
|
+
self._salp1 = salp1
|
|
123
|
+
self._calp1 = calp1
|
|
124
|
+
# allow lat, azimuth and unrolling of lon
|
|
125
|
+
self._caps = caps | Cs._LINE
|
|
126
|
+
|
|
127
|
+
sbet1, cbet1 = gX._sinf1cos2d(_around(lat1))
|
|
128
|
+
self._dn1 = gX._dn(sbet1, cbet1)
|
|
129
|
+
# Evaluate alp0 from sin(alp1) * cos(bet1) = sin(alp0), with alp0
|
|
130
|
+
# in [0, pi/2 - |bet1|]. Alt: calp0 = hypot(sbet1, calp1 * cbet1),
|
|
131
|
+
# but the following is slightly better, consider the case salp1 = 0.
|
|
132
|
+
self._salp0, self._calp0 = _sin1cos2(salp1, calp1, sbet1, cbet1)
|
|
133
|
+
self._k2 = self._calp0**2 * gX.ep2
|
|
134
|
+
# Evaluate sig with tan(bet1) = tan(sig1) * cos(alp1).
|
|
135
|
+
# sig = 0 is nearest northward crossing of equator.
|
|
136
|
+
# With bet1 = 0, alp1 = pi/2, we have sig1 = 0 (equatorial line).
|
|
137
|
+
# With bet1 = pi/2, alp1 = -pi, sig1 = pi/2
|
|
138
|
+
# With bet1 = -pi/2, alp1 = 0 , sig1 = -pi/2
|
|
139
|
+
# Evaluate omg1 with tan(omg1) = sin(alp0) * tan(sig1).
|
|
140
|
+
# With alp0 in (0, pi/2], quadrants for sig and omg coincide.
|
|
141
|
+
# No atan2(0,0) ambiguity at poles since cbet1 = +epsilon.
|
|
142
|
+
# With alp0 = 0, omg1 = 0 for alp1 = 0, omg1 = pi for alp1 = pi.
|
|
143
|
+
self._somg1 = sbet1 * self._salp0
|
|
144
|
+
self._comg1 = c = (cbet1 * calp1) if (sbet1 or calp1) else _1_0
|
|
145
|
+
# Without normalization we have schi1 = somg1.
|
|
146
|
+
self._cchi1 = gX.f1 * self._dn1 * c
|
|
147
|
+
self._ssig1, self._csig1 = _norm2(sbet1, c) # sig1 in (-pi, pi]
|
|
148
|
+
# _norm2(somg1, comg1) # no need to normalize!
|
|
149
|
+
# _norm2(schi1?, cchi1) # no need to normalize!
|
|
150
|
+
if not (caps & Cs.LINE_OFF):
|
|
151
|
+
_glXs.append(self)
|
|
152
|
+
# no need to pre-compute other attrs based on _Caps.X. All are
|
|
153
|
+
# Property_RO's, computed once and cached/memoized until reset
|
|
154
|
+
# when C4order is changed or Elliptic function reset is invoked.
|
|
155
|
+
|
|
156
|
+
def __del__(self): # XXX use weakref?
|
|
157
|
+
if _glXs: # may be empty or None
|
|
158
|
+
try: # PYCHOK no cover
|
|
159
|
+
_glXs.remove(self)
|
|
160
|
+
except (TypeError, ValueError):
|
|
161
|
+
pass
|
|
162
|
+
self._gX = None
|
|
163
|
+
# _update_all(self) # throws TypeError during Python 2 cleanup
|
|
164
|
+
|
|
165
|
+
def _update(self, updated, *attrs, **unused):
|
|
166
|
+
if updated:
|
|
167
|
+
_update_all(self, *attrs)
|
|
168
|
+
|
|
169
|
+
@Property_RO
|
|
170
|
+
def a1(self):
|
|
171
|
+
'''Get the I{equatorial arc} (C{degrees}), the arc length between
|
|
172
|
+
the northward equatorial crossing and the first point.
|
|
173
|
+
'''
|
|
174
|
+
return _atan2d(self._ssig1, self._csig1) # or NAN
|
|
175
|
+
|
|
176
|
+
equatorarc = a1
|
|
177
|
+
|
|
178
|
+
@Property_RO
|
|
179
|
+
def a13(self):
|
|
180
|
+
'''Get the arc length to reference point 3 (C{degrees}).
|
|
181
|
+
|
|
182
|
+
@see: Methods L{Arc} and L{SetArc}.
|
|
183
|
+
'''
|
|
184
|
+
return self._a13
|
|
185
|
+
|
|
186
|
+
def Arc(self):
|
|
187
|
+
'''Return the arc length to reference point 3 (C{degrees} or C{NAN}).
|
|
188
|
+
|
|
189
|
+
@see: Method L{SetArc} and property L{a13}.
|
|
190
|
+
'''
|
|
191
|
+
return self.a13
|
|
192
|
+
|
|
193
|
+
def ArcPosition(self, a12, outmask=Caps.STANDARD):
|
|
194
|
+
'''Find the position on the line given B{C{a12}}.
|
|
195
|
+
|
|
196
|
+
@arg a12: Spherical arc length from the first point to the
|
|
197
|
+
second point (C{degrees}).
|
|
198
|
+
@kwarg outmask: Bit-or'ed combination of L{Caps} values specifying
|
|
199
|
+
the quantities to be returned.
|
|
200
|
+
|
|
201
|
+
@return: A L{GDict} with up to 12 items C{lat1, lon1, azi1, lat2,
|
|
202
|
+
lon2, azi2, m12, a12, s12, M12, M21, S12} with C{lat1},
|
|
203
|
+
C{lon1}, C{azi1} and arc length C{a12} always included,
|
|
204
|
+
except when C{a12=NAN}.
|
|
205
|
+
|
|
206
|
+
@note: By default, C{B{outmask}=STANDARD}, meaning thc C{lat1},
|
|
207
|
+
C{lon1}, C{azi1}, C{lat2}, C{lon2}, C{azi2}, C{s12} and
|
|
208
|
+
C{a12} entries are returned, except when C{a12=NAN}.
|
|
209
|
+
'''
|
|
210
|
+
return self._GDictPosition(True, a12, outmask)
|
|
211
|
+
|
|
212
|
+
@Property_RO
|
|
213
|
+
def azi0(self):
|
|
214
|
+
'''Get the I{equatorial azimuth}, the azimuth of this geodesic line
|
|
215
|
+
as it crosses the equator in a northward direction (C{degrees90}).
|
|
216
|
+
'''
|
|
217
|
+
return _atan2d(*self.azi0_sincos2) # or NAN
|
|
218
|
+
|
|
219
|
+
equatorazimuth = azi0
|
|
220
|
+
|
|
221
|
+
@Property_RO
|
|
222
|
+
def azi0_sincos2(self):
|
|
223
|
+
'''Get the sine and cosine of the I{equatorial azimuth} (2-tuple C{(sin, cos)}).
|
|
224
|
+
'''
|
|
225
|
+
return self._salp0, self._calp0
|
|
226
|
+
|
|
227
|
+
@Property_RO
|
|
228
|
+
def azi1(self):
|
|
229
|
+
'''Get the azimuth at the first point (compass C{degrees}).
|
|
230
|
+
'''
|
|
231
|
+
return self._azi1
|
|
232
|
+
|
|
233
|
+
@Property_RO
|
|
234
|
+
def azi1_sincos2(self):
|
|
235
|
+
'''Get the sine and cosine of the first point's azimuth (2-tuple C{(sin, cos)}).
|
|
236
|
+
'''
|
|
237
|
+
return self._salp1, self._calp1
|
|
238
|
+
|
|
239
|
+
@Property_RO
|
|
240
|
+
def _B41(self):
|
|
241
|
+
'''(INTERNAL) Cached/memoized.
|
|
242
|
+
'''
|
|
243
|
+
return _cosSeries(self._C4a, self._ssig1, self._csig1)
|
|
244
|
+
|
|
245
|
+
@Property_RO
|
|
246
|
+
def _C4a(self):
|
|
247
|
+
'''(INTERNAL) Cached/memoized.
|
|
248
|
+
'''
|
|
249
|
+
return self.geodesic._C4f_k2(self._k2)
|
|
250
|
+
|
|
251
|
+
@Property_RO
|
|
252
|
+
def _caps_DISTANCE_IN(self):
|
|
253
|
+
'''(INTERNAL) Get C{Caps.DISTANCE_IN} and C{_OUT}.
|
|
254
|
+
'''
|
|
255
|
+
return self.caps & (Caps.DISTANCE_IN & Caps._OUT_MASK)
|
|
256
|
+
|
|
257
|
+
@Property_RO
|
|
258
|
+
def _D0k2(self):
|
|
259
|
+
'''(INTERNAL) Cached/memoized.
|
|
260
|
+
'''
|
|
261
|
+
return self._eF.cD * _2__PI * self._k2
|
|
262
|
+
|
|
263
|
+
@Property_RO
|
|
264
|
+
def _D1(self):
|
|
265
|
+
'''(INTERNAL) Cached/memoized.
|
|
266
|
+
'''
|
|
267
|
+
return self._eF.deltaD(self._ssig1, self._csig1, self._dn1)
|
|
268
|
+
|
|
269
|
+
def Distance(self):
|
|
270
|
+
'''Return the distance to reference point 3 (C{meter} or C{NAN}).
|
|
271
|
+
|
|
272
|
+
@see: Method L{SetDistance} and property L{s13}.
|
|
273
|
+
'''
|
|
274
|
+
return self.s13
|
|
275
|
+
|
|
276
|
+
@Property_RO
|
|
277
|
+
def _E0b(self):
|
|
278
|
+
'''(INTERNAL) Cached/memoized.
|
|
279
|
+
'''
|
|
280
|
+
return self._eF.cE * _2__PI * self.geodesic.b
|
|
281
|
+
|
|
282
|
+
@Property_RO
|
|
283
|
+
def _E1(self):
|
|
284
|
+
'''(INTERNAL) Cached/memoized.
|
|
285
|
+
'''
|
|
286
|
+
return self._eF.deltaE(self._ssig1, self._csig1, self._dn1)
|
|
287
|
+
|
|
288
|
+
@Property_RO
|
|
289
|
+
def _eF(self):
|
|
290
|
+
'''(INTERNAL) Cached/memoized C{Elliptic} function.
|
|
291
|
+
'''
|
|
292
|
+
# see .gx.GeodesicExact._ef_reset_k2
|
|
293
|
+
return _MODS.elliptic.Elliptic(k2=-self._k2, alpha2=-self.geodesic.ep2)
|
|
294
|
+
|
|
295
|
+
def _GDictPosition(self, arcmode, s12_a12, outmask=Caps.STANDARD): # MCCABE 17
|
|
296
|
+
'''(INTERNAL) Generate a new position along the geodesic.
|
|
297
|
+
|
|
298
|
+
@return: A L{GDict} with up to 12 items C{lat1, lon1, azi1, lat2,
|
|
299
|
+
lon2, azi2, m12, a12, s12, M12, M21, S12} with C{lat1},
|
|
300
|
+
C{lon1}, C{azi1} and arc length C{a12} always included,
|
|
301
|
+
except when C{a12=NAN}.
|
|
302
|
+
'''
|
|
303
|
+
|
|
304
|
+
r = GDict(a12=NAN, s12=NAN) # note both a12 and s12, always
|
|
305
|
+
if not (arcmode or self._caps_DISTANCE_IN): # PYCHOK no cover
|
|
306
|
+
return r # Uninitialized or impossible distance requested
|
|
307
|
+
|
|
308
|
+
Cs = Caps
|
|
309
|
+
if self._debug: # PYCHOK no cover
|
|
310
|
+
outmask |= self._debug & Cs._DEBUG_DIRECT_LINE
|
|
311
|
+
outmask &= self._caps & Cs._OUT_MASK
|
|
312
|
+
|
|
313
|
+
eF = self._eF
|
|
314
|
+
gX = self.geodesic # ._gX
|
|
315
|
+
|
|
316
|
+
if arcmode:
|
|
317
|
+
# s12_a12 is spherical arc length
|
|
318
|
+
E2 = _0_0
|
|
319
|
+
sig12 = radians(s12_a12)
|
|
320
|
+
if _K_2_0:
|
|
321
|
+
ssig12, csig12 = sincos2(sig12) # utily, no NEG0
|
|
322
|
+
else: # PYCHOK no cover
|
|
323
|
+
a = fabs(s12_a12) # 0 <= fabs(_remainder(s12_a12, _180_0)) <= 90
|
|
324
|
+
a -= floor(a / _180_0) * _180_0 # 0 <= 0 < 180
|
|
325
|
+
ssig12 = _0_0 if a == 0 else sin(sig12)
|
|
326
|
+
csig12 = _0_0 if a == 90 else cos(sig12)
|
|
327
|
+
else: # s12_a12 is distance
|
|
328
|
+
t = s12_a12 / self._E0b
|
|
329
|
+
s, c = _sincos2(t) # tau12
|
|
330
|
+
# tau2 = tau1 + tau12
|
|
331
|
+
E2 = -eF.deltaEinv(*_sincos12(-s, c, *self._stau1_ctau1))
|
|
332
|
+
sig12 = fsum1f_(self._E1, -E2, t) # == t - (E2 - E1)
|
|
333
|
+
ssig12, csig12 = _sincos2(sig12)
|
|
334
|
+
|
|
335
|
+
salp0, calp0 = self._salp0, self._calp0
|
|
336
|
+
ssig1, csig1 = self._ssig1, self._csig1
|
|
337
|
+
|
|
338
|
+
# sig2 = sig1 + sig12
|
|
339
|
+
ssig2, csig2 = _sincos12(-ssig12, csig12, ssig1, csig1)
|
|
340
|
+
dn2 = eF.fDelta(ssig2, csig2)
|
|
341
|
+
# sin(bet2) = cos(alp0) * sin(sig2) and
|
|
342
|
+
# cbet2 = hypot(salp0, calp0 * csig2). Alt:
|
|
343
|
+
# cbet2 = hypot(csig2, salp0 * ssig2)
|
|
344
|
+
sbet2, cbet2 = _sin1cos2(calp0, salp0, csig2, ssig2)
|
|
345
|
+
if cbet2 == 0: # salp0 = 0, csig2 = 0, break degeneracy
|
|
346
|
+
cbet2 = csig2 = _TINY
|
|
347
|
+
# tan(alp0) = cos(sig2) * tan(alp2)
|
|
348
|
+
salp2 = salp0
|
|
349
|
+
calp2 = calp0 * csig2 # no need to normalize
|
|
350
|
+
|
|
351
|
+
if (outmask & Cs.DISTANCE):
|
|
352
|
+
if arcmode: # or f_0_01
|
|
353
|
+
E2 = eF.deltaE(ssig2, csig2, dn2)
|
|
354
|
+
# AB1 = _E0 * (E2 - _E1)
|
|
355
|
+
# s12 = _b * (_E0 * sig12 + AB1)
|
|
356
|
+
# = _b * _E0 * (sig12 + (E2 - _E1))
|
|
357
|
+
# = _b * _E0 * (E2 - _E1 + sig12)
|
|
358
|
+
s12 = self._E0b * fsum1f_(E2, -self._E1, sig12)
|
|
359
|
+
else:
|
|
360
|
+
s12 = s12_a12
|
|
361
|
+
r.set_(s12=s12)
|
|
362
|
+
|
|
363
|
+
if (outmask & Cs._DEBUG_DIRECT_LINE): # PYCHOK no cover
|
|
364
|
+
r.set_(sig12=sig12, dn2=dn2, b=gX.b, e2=gX.e2, f1=gX.f1,
|
|
365
|
+
E0b=self._E0b, E1=self._E1, E2=E2, eFk2=eF.k2, eFa2=eF.alpha2)
|
|
366
|
+
|
|
367
|
+
if (outmask & Cs.LONGITUDE):
|
|
368
|
+
schi1 = self._somg1
|
|
369
|
+
cchi1 = self._cchi1
|
|
370
|
+
schi2 = ssig2 * salp0
|
|
371
|
+
cchi2 = gX.f1 * dn2 * csig2 # schi2 = somg2 without normalization
|
|
372
|
+
lam12 = salp0 * self._H0e2_f1 * fsum1f_(eF.deltaH(ssig2, csig2, dn2),
|
|
373
|
+
-self._H1, sig12)
|
|
374
|
+
if (outmask & Cs.LONG_UNROLL):
|
|
375
|
+
_a, t = atan2, _copysign_1_0(salp0) # east-going?
|
|
376
|
+
tchi1 = t * schi1
|
|
377
|
+
tchi2 = t * schi2
|
|
378
|
+
chi12 = t * fsum1f_(_a(ssig1, csig1), -_a(ssig2, csig2),
|
|
379
|
+
_a(tchi2, cchi2), -_a(tchi1, cchi1), sig12)
|
|
380
|
+
lon2 = self.lon1 + degrees(chi12 - lam12)
|
|
381
|
+
else:
|
|
382
|
+
chi12 = atan2(*_sincos12(schi1, cchi1, schi2, cchi2))
|
|
383
|
+
lon2 = _norm180(self._lon1_norm180 + _norm180(degrees(chi12 - lam12)))
|
|
384
|
+
r.set_(lon2=lon2)
|
|
385
|
+
if (outmask & Cs._DEBUG_DIRECT_LINE): # PYCHOK no cover
|
|
386
|
+
r.set_(ssig2=ssig2, chi12=chi12, H0e2_f1=self._H0e2_f1,
|
|
387
|
+
csig2=csig2, lam12=lam12, H1=self._H1)
|
|
388
|
+
|
|
389
|
+
if (outmask & Cs.LATITUDE):
|
|
390
|
+
r.set_(lat2=_atan2d(sbet2, gX.f1 * cbet2))
|
|
391
|
+
|
|
392
|
+
if (outmask & Cs.AZIMUTH):
|
|
393
|
+
r.set_(azi2=_atan2d_reverse(salp2, calp2, reverse=outmask & Cs.REVERSE2))
|
|
394
|
+
|
|
395
|
+
if (outmask & Cs._REDUCEDLENGTH_GEODESICSCALE):
|
|
396
|
+
dn1 = self._dn1
|
|
397
|
+
J12 = self._D0k2 * fsumf_(eF.deltaD(ssig2, csig2, dn2), -self._D1, sig12)
|
|
398
|
+
if (outmask & Cs._DEBUG_DIRECT_LINE): # PYCHOK no cover
|
|
399
|
+
r.set_(ssig1=ssig1, dn1=dn1, D0k2=self._D0k2,
|
|
400
|
+
csig1=csig1, J12=J12, D1=self._D1)
|
|
401
|
+
if (outmask & Cs.REDUCEDLENGTH):
|
|
402
|
+
# Add parens around (csig1 * ssig2) and (ssig1 * csig2) to
|
|
403
|
+
# ensure accurate cancellation in the case of coincident points.
|
|
404
|
+
r.set_(m12=gX.b * fsum1f_(dn2 * (csig1 * ssig2),
|
|
405
|
+
-dn1 * (ssig1 * csig2),
|
|
406
|
+
-J12 * (csig1 * csig2)))
|
|
407
|
+
if (outmask & Cs.GEODESICSCALE):
|
|
408
|
+
t = self._k2 * (ssig2 - ssig1) * (ssig2 + ssig1) / (dn2 + dn1)
|
|
409
|
+
r.set_(M12=csig12 + ssig1 * (t * ssig2 - csig2 * J12) / dn1,
|
|
410
|
+
M21=csig12 - ssig2 * (t * ssig1 - csig1 * J12) / dn2)
|
|
411
|
+
|
|
412
|
+
if (outmask & Cs.AREA):
|
|
413
|
+
A4 = salp0 * calp0
|
|
414
|
+
if A4:
|
|
415
|
+
# tan(alp) = tan(alp0) * sec(sig)
|
|
416
|
+
# tan(alp2-alp1) = (tan(alp2) - tan(alp1)) / (tan(alp2) * tan(alp1) + 1)
|
|
417
|
+
# = calp0 * salp0 * (csig1 - csig2) / (salp0^2 + calp0^2 * csig1 * csig2)
|
|
418
|
+
# If csig12 > 0, write
|
|
419
|
+
# csig1 - csig2 = ssig12 * (csig1 * ssig12 / (1 + csig12) + ssig1)
|
|
420
|
+
# else
|
|
421
|
+
# csig1 - csig2 = csig1 * (1 - csig12) + ssig12 * ssig1
|
|
422
|
+
# No need to normalize
|
|
423
|
+
salp12 = (((ssig12 * csig1 / (_1_0 + csig12) + ssig1) * ssig12) if csig12 > 0 else
|
|
424
|
+
(csig1 * (_1_0 - csig12) + ssig1 * ssig12)) * A4
|
|
425
|
+
calp12 = salp0**2 + calp0**2 * csig1 * csig2
|
|
426
|
+
A4 *= gX._e2a2
|
|
427
|
+
B41 = self._B41
|
|
428
|
+
B42 = _cosSeries(self._C4a, ssig2, csig2)
|
|
429
|
+
S12 = (B42 - B41) * A4
|
|
430
|
+
else:
|
|
431
|
+
S12 = A4 = B41 = B42 = _0_0
|
|
432
|
+
# alp12 = alp2 - alp1, used in atan2 so no need to normalize
|
|
433
|
+
salp12, calp12 = _sincos12(self._salp1, self._calp1, salp2, calp2)
|
|
434
|
+
# We used to include some patch up code that purported to deal
|
|
435
|
+
# with nearly meridional geodesics properly. However, this turned
|
|
436
|
+
# out to be wrong once salp1 = -0 was allowed (via InverseLine).
|
|
437
|
+
# In fact, the calculation of {s,c}alp12 was already correct
|
|
438
|
+
# (following the IEEE rules for handling signed zeros). So,
|
|
439
|
+
# the patch up code was unnecessary (as well as dangerous).
|
|
440
|
+
if (outmask & Cs._DEBUG_DIRECT_LINE): # PYCHOK no cover
|
|
441
|
+
r.set_(salp12=salp12, salp0=salp0, B41=B41, A4=A4,
|
|
442
|
+
calp12=calp12, calp0=calp0, B42=B42, c2=gX.c2)
|
|
443
|
+
S12 += gX.c2 * atan2(salp12, calp12)
|
|
444
|
+
r.set_(S12=S12)
|
|
445
|
+
|
|
446
|
+
r.set_(a12=s12_a12 if arcmode else degrees(sig12),
|
|
447
|
+
lat1=self.lat1, # == _fix90(lat1)
|
|
448
|
+
lon1=self.lon1 if (outmask & Cs.LONG_UNROLL) else self._lon1_norm180,
|
|
449
|
+
azi1=_norm180(self.azi1))
|
|
450
|
+
return r
|
|
451
|
+
|
|
452
|
+
def _GenPosition(self, arcmode, s12_a12, outmask):
|
|
453
|
+
'''(INTERNAL) Generate a new position along the geodesic.
|
|
454
|
+
|
|
455
|
+
@return: L{Direct9Tuple}C{(a12, lat2, lon2, azi2,
|
|
456
|
+
s12, m12, M12, M21, S12)}.
|
|
457
|
+
'''
|
|
458
|
+
r = self._GDictPosition(arcmode, s12_a12, outmask)
|
|
459
|
+
return r.toDirect9Tuple()
|
|
460
|
+
|
|
461
|
+
def _GenSet(self, arcmode, s13_a13):
|
|
462
|
+
'''(INTERNAL) Aka C++ C{GenSetDistance}.
|
|
463
|
+
'''
|
|
464
|
+
if arcmode:
|
|
465
|
+
self.SetArc(s13_a13)
|
|
466
|
+
else:
|
|
467
|
+
self.SetDistance(s13_a13)
|
|
468
|
+
return self # for gx.GeodesicExact.InverseLine and -._GenDirectLine
|
|
469
|
+
|
|
470
|
+
@Property_RO
|
|
471
|
+
def geodesic(self):
|
|
472
|
+
'''Get the I{exact} geodesic (L{GeodesicExact}).
|
|
473
|
+
'''
|
|
474
|
+
_xGeodesicExact(geodesic=self._gX)
|
|
475
|
+
return self._gX
|
|
476
|
+
|
|
477
|
+
def Intersecant2(self, lat0, lon0, radius, tol=_TOL):
|
|
478
|
+
'''Compute the intersection(s) of this geodesic line and a circle.
|
|
479
|
+
|
|
480
|
+
@arg lat0: Latitude of the circle center (C{degrees}).
|
|
481
|
+
@arg lon0: Longitude of the circle center (C{degrees}).
|
|
482
|
+
@arg radius: Radius of the circle (C{meter}, conventionally).
|
|
483
|
+
@kwarg tol: Convergence tolerance (C{scalar}).
|
|
484
|
+
|
|
485
|
+
@return: 2-Tuple C{(P, Q)} with both intersections (representing
|
|
486
|
+
a geodesic chord), each a L{GDict} from method L{Position}
|
|
487
|
+
extended to 14 items by C{lon0, lat0, azi0, a02, s02, at}
|
|
488
|
+
with the circle center C{lat0}, C{lon0}, azimuth C{azi0}
|
|
489
|
+
at, distance C{a02} in C{degrees} and C{s02} in C{meter}
|
|
490
|
+
along the geodesic from the circle center to the intersection
|
|
491
|
+
C{lat2}, C{lon2} and the angle C{at} between the geodesic
|
|
492
|
+
and this line at the intersection. The geodesic azimuth
|
|
493
|
+
at the intersection is C{(at + azi2)}. If this geodesic
|
|
494
|
+
line is tangential to the circle, both points are the same
|
|
495
|
+
L{GDict} instance.
|
|
496
|
+
|
|
497
|
+
@raise IntersectionError: The circle and this geodesic line do not
|
|
498
|
+
intersect, no I{perpencular} geodetic
|
|
499
|
+
intersection or no convergence.
|
|
500
|
+
|
|
501
|
+
@raise UnitError: Invalid B{C{radius}}.
|
|
502
|
+
'''
|
|
503
|
+
try:
|
|
504
|
+
return _MODS.geodesicw._Intersecant2(self, lat0, lon0, radius, tol=tol)
|
|
505
|
+
except (TypeError, ValueError) as x:
|
|
506
|
+
raise _xError(x, lat0, lon0, radius, tol=_TOL)
|
|
507
|
+
|
|
508
|
+
@Property_RO
|
|
509
|
+
def _H0e2_f1(self):
|
|
510
|
+
'''(INTERNAL) Cached/memoized.
|
|
511
|
+
'''
|
|
512
|
+
return self._eF.cH * _2__PI * self.geodesic._e2_f1
|
|
513
|
+
|
|
514
|
+
@Property_RO
|
|
515
|
+
def _H1(self):
|
|
516
|
+
'''(INTERNAL) Cached/memoized.
|
|
517
|
+
'''
|
|
518
|
+
return self._eF.deltaH(self._ssig1, self._csig1, self._dn1)
|
|
519
|
+
|
|
520
|
+
@Property_RO
|
|
521
|
+
def lat1(self):
|
|
522
|
+
'''Get the latitude of the first point (C{degrees}).
|
|
523
|
+
'''
|
|
524
|
+
return self._lat1
|
|
525
|
+
|
|
526
|
+
@Property_RO
|
|
527
|
+
def lon1(self):
|
|
528
|
+
'''Get the longitude of the first point (C{degrees}).
|
|
529
|
+
'''
|
|
530
|
+
return self._lon1
|
|
531
|
+
|
|
532
|
+
@Property_RO
|
|
533
|
+
def _lon1_norm180(self):
|
|
534
|
+
'''(INTERNAL) Cached/memoized.
|
|
535
|
+
'''
|
|
536
|
+
return _norm180(self._lon1)
|
|
537
|
+
|
|
538
|
+
def PlumbTo(self, lat0, lon0, est=None, tol=_TOL):
|
|
539
|
+
'''Compute the I{perpendicular} intersection of this geodesic line
|
|
540
|
+
and a geodesic from the given point.
|
|
541
|
+
|
|
542
|
+
@arg lat0: Latitude of the point (C{degrees}).
|
|
543
|
+
@arg lon0: Longitude of the point (C{degrees}).
|
|
544
|
+
@kwarg est: Optional, initial estimate for the distance C{s12} of
|
|
545
|
+
the intersection I{along} this geodesic line (C{meter}).
|
|
546
|
+
@kwarg tol: Convergence tolerance (C(meter)).
|
|
547
|
+
|
|
548
|
+
@return: The intersection point on this geodesic line, a L{GDict}
|
|
549
|
+
from method L{Position} extended to 14 items C{lat1, lon1,
|
|
550
|
+
azi1, lat2, lon2, azi2, a12, s12, lat0, lon0, azi0, a02,
|
|
551
|
+
s02, at} with distance C{a02} in C{degrees} and C{s02} in
|
|
552
|
+
C{meter} between the given C{lat0, lon0} point and the
|
|
553
|
+
intersection C{lat2, lon2}, azimuth C{azi0} at the given
|
|
554
|
+
point and C{at} the (perpendicular) angle between the
|
|
555
|
+
geodesic and this line at the intersection. The geodesic
|
|
556
|
+
azimuth at the intersection is C{(at + azi2)}. See method
|
|
557
|
+
L{Position} for further details.
|
|
558
|
+
|
|
559
|
+
@see: Methods C{Intersecant2}, C{Intersection} and C{Position}.
|
|
560
|
+
'''
|
|
561
|
+
return _MODS.geodesicw._PlumbTo(self, lat0, lon0, est=est, tol=tol)
|
|
562
|
+
|
|
563
|
+
def Position(self, s12, outmask=Caps.STANDARD):
|
|
564
|
+
'''Find the position on the line given B{C{s12}}.
|
|
565
|
+
|
|
566
|
+
@arg s12: Distance from this this line's first point (C{meter}).
|
|
567
|
+
@kwarg outmask: Bit-or'ed combination of L{Caps} values specifying
|
|
568
|
+
the quantities to be returned.
|
|
569
|
+
|
|
570
|
+
@return: A L{GDict} with up to 12 items C{lat1, lon1, azi1, lat2,
|
|
571
|
+
lon2, azi2, m12, a12, s12, M12, M21, S12} with C{lat1},
|
|
572
|
+
C{lon1}, C{azi1} and arc length C{a12} always included,
|
|
573
|
+
except when C{a12=NAN}.
|
|
574
|
+
|
|
575
|
+
@note: By default, C{B{outmask}=STANDARD}, meaning thc C{lat1},
|
|
576
|
+
C{lon1}, C{azi1}, C{lat2}, C{lon2}, C{azi2}, C{s12} and
|
|
577
|
+
C{a12} entries are returned, except when C{a12=NAN}.
|
|
578
|
+
|
|
579
|
+
@note: This L{GeodesicLineExact} instance must have been
|
|
580
|
+
constructed with capability C{Caps.DISTANCE_IN} set.
|
|
581
|
+
'''
|
|
582
|
+
return self._GDictPosition(False, s12, outmask)
|
|
583
|
+
|
|
584
|
+
@Property_RO
|
|
585
|
+
def s13(self):
|
|
586
|
+
'''Get the distance to reference point 3 (C{meter} or C{NAN}).
|
|
587
|
+
|
|
588
|
+
@see: Methods L{Distance} and L{SetDistance}.
|
|
589
|
+
'''
|
|
590
|
+
return self._s13
|
|
591
|
+
|
|
592
|
+
def SetArc(self, a13):
|
|
593
|
+
'''Set reference point 3 in terms relative to the first point.
|
|
594
|
+
|
|
595
|
+
@arg a13: Spherical arc length from the first to the reference
|
|
596
|
+
point (C{degrees}).
|
|
597
|
+
|
|
598
|
+
@return: The distance C{s13} (C{meter}) between the first and
|
|
599
|
+
the reference point or C{NAN}.
|
|
600
|
+
'''
|
|
601
|
+
if self._a13 != a13:
|
|
602
|
+
self._a13 = a13
|
|
603
|
+
self._s13 = self._GDictPosition(True, a13, Caps.DISTANCE).s12 # if a13 else _0_0
|
|
604
|
+
_update_all(self)
|
|
605
|
+
return self._s13
|
|
606
|
+
|
|
607
|
+
def SetDistance(self, s13):
|
|
608
|
+
'''Set reference point 3 in terms relative to the first point.
|
|
609
|
+
|
|
610
|
+
@arg s13: Distance from the first to the reference point (C{meter}).
|
|
611
|
+
|
|
612
|
+
@return: The arc length C{a13} (C{degrees}) between the first
|
|
613
|
+
and the reference point or C{NAN}.
|
|
614
|
+
'''
|
|
615
|
+
if self._s13 != s13:
|
|
616
|
+
self._s13 = s13
|
|
617
|
+
self._a13 = self._GDictPosition(False, s13, 0).a12 if s13 else _0_0
|
|
618
|
+
_update_all(self)
|
|
619
|
+
return self._a13 # NAN for GeodesicLineExact without Cap.DISTANCE_IN
|
|
620
|
+
|
|
621
|
+
@Property_RO
|
|
622
|
+
def _stau1_ctau1(self):
|
|
623
|
+
'''(INTERNAL) Cached/memoized.
|
|
624
|
+
'''
|
|
625
|
+
s, c = _sincos2(self._E1)
|
|
626
|
+
# tau1 = sig1 + B11
|
|
627
|
+
return _sincos12(-s, c, self._ssig1, self._csig1)
|
|
628
|
+
# unnecessary because Einv inverts E
|
|
629
|
+
# return -self._eF.deltaEinv(stau1, ctau1)
|
|
630
|
+
|
|
631
|
+
def toStr(self, prec=6, sep=_COMMASPACE_, **unused): # PYCHOK signature
|
|
632
|
+
'''Return this C{GeodesicLineExact} as string.
|
|
633
|
+
|
|
634
|
+
@kwarg prec: The C{float} precision, number of decimal digits (0..9).
|
|
635
|
+
Trailing zero decimals are stripped for B{C{prec}} values
|
|
636
|
+
of 1 and above, but kept for negative B{C{prec}} values.
|
|
637
|
+
@kwarg sep: Separator to join (C{str}).
|
|
638
|
+
|
|
639
|
+
@return: C{GeodesicLineExact} (C{str}).
|
|
640
|
+
'''
|
|
641
|
+
d = dict(geodesic=self.geodesic,
|
|
642
|
+
lat1=self.lat1, lon1=self.lon1, azi1=self.azi1,
|
|
643
|
+
a13=self.a13, s13=self.s13)
|
|
644
|
+
return sep.join(_MODS.streprs.pairs(d, prec=prec))
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
__all__ += _ALL_DOCS(_GeodesicLineExact)
|
|
648
|
+
|
|
649
|
+
# **) MIT License
|
|
650
|
+
#
|
|
651
|
+
# Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
|
|
652
|
+
#
|
|
653
|
+
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
654
|
+
# copy of this software and associated documentation files (the "Software"),
|
|
655
|
+
# to deal in the Software without restriction, including without limitation
|
|
656
|
+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
657
|
+
# and/or sell copies of the Software, and to permit persons to whom the
|
|
658
|
+
# Software is furnished to do so, subject to the following conditions:
|
|
659
|
+
#
|
|
660
|
+
# The above copyright notice and this permission notice shall be included
|
|
661
|
+
# in all copies or substantial portions of the Software.
|
|
662
|
+
#
|
|
663
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
664
|
+
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
665
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
666
|
+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
667
|
+
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
668
|
+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
669
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|