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,1224 @@
|
|
|
1
|
+
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
u'''(INTERNAL) Private ellipsoidal base classes C{CartesianEllipsoidalBase}
|
|
5
|
+
and C{LatLonEllipsoidalBase}.
|
|
6
|
+
|
|
7
|
+
A pure Python implementation of geodesy tools for ellipsoidal earth models,
|
|
8
|
+
transcoded in part from JavaScript originals by I{(C) Chris Veness 2005-2016}
|
|
9
|
+
and published under the same MIT Licence**, see for example U{latlon-ellipsoidal
|
|
10
|
+
<https://www.Movable-Type.co.UK/scripts/geodesy/docs/latlon-ellipsoidal.js.html>}.
|
|
11
|
+
'''
|
|
12
|
+
# make sure int/int division yields float quotient, see .basics
|
|
13
|
+
from __future__ import division as _; del _ # PYCHOK semicolon
|
|
14
|
+
|
|
15
|
+
# from pygeodesy.basics import _xinstanceof # from .datums
|
|
16
|
+
from pygeodesy.constants import EPS, EPS0, EPS1, _0_0, _0_5
|
|
17
|
+
from pygeodesy.cartesianBase import CartesianBase # PYCHOK used!
|
|
18
|
+
from pygeodesy.datums import Datum, Datums, _earth_ellipsoid, _ellipsoidal_datum, \
|
|
19
|
+
Transform, _WGS84, _EWGS84, _xinstanceof # _spherical_datum
|
|
20
|
+
# from pygeodesy.ellipsoids import _EWGS84 # from .datums
|
|
21
|
+
from pygeodesy.errors import _incompatible, _IsnotError, RangeError, _TypeError, \
|
|
22
|
+
_ValueError, _xattr, _xellipsoidal, _xError, _xkwds, \
|
|
23
|
+
_xkwds_not
|
|
24
|
+
# from pygeodesy.fmath import favg # _MODS
|
|
25
|
+
from pygeodesy.interns import NN, _COMMA_, _ellipsoidal_
|
|
26
|
+
from pygeodesy.latlonBase import LatLonBase, _trilaterate5, fabs, _Wrap
|
|
27
|
+
from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS
|
|
28
|
+
# from pygeodesy.lcc import toLcc # _MODS
|
|
29
|
+
# from pygeodesy.named import notOverloaded # _MODS
|
|
30
|
+
# from pygeodesy.namedTuples import Vector3Tuple # _MODS
|
|
31
|
+
from pygeodesy.props import deprecated_method, deprecated_property_RO, \
|
|
32
|
+
Property_RO, property_doc_, property_RO, _update_all
|
|
33
|
+
# from pygeodesy.trf import _eT0Ds4 # _MODS
|
|
34
|
+
from pygeodesy.units import Epoch, _isDegrees, Radius_, _1mm as _TOL_M
|
|
35
|
+
# from pygeodesy.utily import _Wrap # from .latlonBase
|
|
36
|
+
|
|
37
|
+
# from math import fabs # from .latlonBase
|
|
38
|
+
|
|
39
|
+
__all__ = _ALL_LAZY.ellipsoidalBase
|
|
40
|
+
__version__ = '24.03.09'
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class CartesianEllipsoidalBase(CartesianBase):
|
|
44
|
+
'''(INTERNAL) Base class for ellipsoidal C{Cartesian}s.
|
|
45
|
+
'''
|
|
46
|
+
_datum = _WGS84 # L{Datum}
|
|
47
|
+
_epoch = None # overriding .reframe.epoch (C{float})
|
|
48
|
+
_reframe = None # reference frame (L{RefFrame})
|
|
49
|
+
|
|
50
|
+
def __init__(self, x_xyz, y=None, z=None, datum=None, reframe=None,
|
|
51
|
+
epoch=None, ll=None, name=NN):
|
|
52
|
+
'''New ellispoidal C{Cartesian...}.
|
|
53
|
+
|
|
54
|
+
@kwarg reframe: Optional reference frame (L{RefFrame}).
|
|
55
|
+
@kwarg epoch: Optional epoch to observe for B{C{reframe}} (C{scalar}),
|
|
56
|
+
a non-zero, fractional calendar year; silently ignored
|
|
57
|
+
if C{B{reframe}=None}.
|
|
58
|
+
|
|
59
|
+
@raise TypeError: Non-scalar B{C{x_xyz}}, B{C{y}} or B{C{z}} coordinate
|
|
60
|
+
or B{C{x_xyz}} not a C{Cartesian} L{Ecef9Tuple},
|
|
61
|
+
L{Vector3Tuple} or L{Vector4Tuple} or B{C{datum}} is
|
|
62
|
+
not a L{Datum}, B{C{reframe}} is not a L{RefFrame} or
|
|
63
|
+
B{C{epoch}} is not C{scalar} non-zero.
|
|
64
|
+
|
|
65
|
+
@see: Super-class L{CartesianBase<CartesianBase.__init__>} for more details.
|
|
66
|
+
'''
|
|
67
|
+
CartesianBase.__init__(self, x_xyz, y=y, z=z, datum=datum, ll=ll, name=name)
|
|
68
|
+
if reframe:
|
|
69
|
+
self.reframe = reframe
|
|
70
|
+
self.epoch = epoch
|
|
71
|
+
|
|
72
|
+
# def __matmul__(self, other): # PYCHOK Python 3.5+
|
|
73
|
+
# '''Return C{NotImplemented} for C{c_ = c @ datum}, C{c_ = c @ reframe} and C{c_ = c @ Transform}.
|
|
74
|
+
# '''
|
|
75
|
+
# RefFrame = _MODS.trf.RefFrame
|
|
76
|
+
# return NotImplemented if isinstance(other, (Datum, RefFrame, Transform)) else \
|
|
77
|
+
# _NotImplemented(self, other)
|
|
78
|
+
|
|
79
|
+
@deprecated_method
|
|
80
|
+
def convertRefFrame(self, reframe2, reframe, epoch=None):
|
|
81
|
+
'''DEPRECATED, use method L{toRefFrame}.'''
|
|
82
|
+
return self.toRefFrame(reframe2, reframe=reframe, epoch=epoch)
|
|
83
|
+
|
|
84
|
+
@property_RO
|
|
85
|
+
def ellipsoidalCartesian(self):
|
|
86
|
+
'''Get this C{Cartesian}'s ellipsoidal class.
|
|
87
|
+
'''
|
|
88
|
+
return type(self)
|
|
89
|
+
|
|
90
|
+
@property_doc_(''' this cartesian's observed or C{reframe} epoch (C{float}).''')
|
|
91
|
+
def epoch(self):
|
|
92
|
+
'''Get this cartesian's observed or C{reframe} epoch (C{Epoch}) or C{None}.
|
|
93
|
+
'''
|
|
94
|
+
return self._epoch or (self.reframe.epoch if self.reframe else None)
|
|
95
|
+
|
|
96
|
+
@epoch.setter # PYCHOK setter!
|
|
97
|
+
def epoch(self, epoch):
|
|
98
|
+
'''Set or clear this cartesian's observed epoch, a fractional
|
|
99
|
+
calendar year (L{Epoch}, C{scalar} or C{str}) or C{None}.
|
|
100
|
+
|
|
101
|
+
@raise TRFError: Invalid B{C{epoch}}.
|
|
102
|
+
'''
|
|
103
|
+
self._epoch = None if epoch is None else Epoch(epoch)
|
|
104
|
+
|
|
105
|
+
def intersections2(self, radius, center2, radius2, sphere=True,
|
|
106
|
+
Vector=None, **Vector_kwds):
|
|
107
|
+
'''Compute the intersection of two spheres or circles, each defined by a
|
|
108
|
+
cartesian center point and a radius.
|
|
109
|
+
|
|
110
|
+
@arg radius: Radius of this sphere or circle (same units as this point's
|
|
111
|
+
coordinates).
|
|
112
|
+
@arg center2: Center of the second sphere or circle (C{Cartesian}, L{Vector3d},
|
|
113
|
+
C{Vector3Tuple} or C{Vector4Tuple}).
|
|
114
|
+
@arg radius2: Radius of the second sphere or circle (same units as this and
|
|
115
|
+
the B{C{other}} point's coordinates).
|
|
116
|
+
@kwarg sphere: If C{True} compute the center and radius of the intersection
|
|
117
|
+
of two I{spheres}. If C{False}, ignore the C{z}-component and
|
|
118
|
+
compute the intersection of two I{circles} (C{bool}).
|
|
119
|
+
@kwarg Vector: Class to return intersections (C{Cartesian}, L{Vector3d} or
|
|
120
|
+
C{Vector3Tuple}) or C{None} for an instance of this (sub-)class.
|
|
121
|
+
@kwarg Vector_kwds: Optional, additional B{C{Vector}} keyword arguments,
|
|
122
|
+
ignored if C{B{Vector} is None}.
|
|
123
|
+
|
|
124
|
+
@return: If B{C{sphere}} is C{True}, a 2-tuple of the C{center} and C{radius}
|
|
125
|
+
of the intersection of the I{spheres}. The C{radius} is C{0.0} for
|
|
126
|
+
abutting spheres (and the C{center} is aka the I{radical center}).
|
|
127
|
+
|
|
128
|
+
If B{C{sphere}} is C{False}, a 2-tuple with the two intersection
|
|
129
|
+
points of the I{circles}. For abutting circles, both points are
|
|
130
|
+
the same instance, aka the I{radical center}.
|
|
131
|
+
|
|
132
|
+
@raise IntersectionError: Concentric, invalid or non-intersecting spheres or circles.
|
|
133
|
+
|
|
134
|
+
@raise TypeError: Invalid B{C{center2}}.
|
|
135
|
+
|
|
136
|
+
@raise UnitError: Invalid B{C{radius}} or B{C{radius2}}.
|
|
137
|
+
|
|
138
|
+
@see: U{Sphere-Sphere<https://MathWorld.Wolfram.com/Sphere-SphereIntersection.html>},
|
|
139
|
+
U{Circle-Circle<https://MathWorld.Wolfram.com/Circle-CircleIntersection.html>}
|
|
140
|
+
Intersection and function L{pygeodesy.radical2}.
|
|
141
|
+
'''
|
|
142
|
+
try:
|
|
143
|
+
return _MODS.vector3d._intersects2(self, Radius_(radius=radius),
|
|
144
|
+
center2, Radius_(radius2=radius2),
|
|
145
|
+
sphere=sphere, clas=self.classof,
|
|
146
|
+
Vector=Vector, **Vector_kwds)
|
|
147
|
+
except (TypeError, ValueError) as x:
|
|
148
|
+
raise _xError(x, center=self, radius=radius, center2=center2, radius2=radius2)
|
|
149
|
+
|
|
150
|
+
@property_doc_(''' this cartesian's reference frame (L{RefFrame}).''')
|
|
151
|
+
def reframe(self):
|
|
152
|
+
'''Get this cartesian's reference frame (L{RefFrame}) or C{None}.
|
|
153
|
+
'''
|
|
154
|
+
return self._reframe
|
|
155
|
+
|
|
156
|
+
@reframe.setter # PYCHOK setter!
|
|
157
|
+
def reframe(self, reframe):
|
|
158
|
+
'''Set or clear this cartesian's reference frame (L{RefFrame}) or C{None}.
|
|
159
|
+
|
|
160
|
+
@raise TypeError: The B{C{reframe}} is not a L{RefFrame}.
|
|
161
|
+
'''
|
|
162
|
+
_set_reframe(self, reframe)
|
|
163
|
+
|
|
164
|
+
def toLatLon(self, datum=None, height=None, **LatLon_and_kwds): # PYCHOK signature
|
|
165
|
+
'''Convert this cartesian to a I{geodetic} (lat-/longitude) point.
|
|
166
|
+
|
|
167
|
+
@see: Method L{toLatLon<cartesianBase.CartesianBase.toLatLon>}
|
|
168
|
+
for further details.
|
|
169
|
+
'''
|
|
170
|
+
kwds = LatLon_and_kwds
|
|
171
|
+
if kwds:
|
|
172
|
+
kwds = _xkwds(kwds, reframe=self.reframe, epoch=self.epoch)
|
|
173
|
+
return CartesianBase.toLatLon(self, datum=datum, height=height, **kwds)
|
|
174
|
+
|
|
175
|
+
def toRefFrame(self, reframe2, reframe=None, epoch=None, epoch2=None, name=NN):
|
|
176
|
+
'''Convert this point to an other reference frame and epoch.
|
|
177
|
+
|
|
178
|
+
@arg reframe2: Reference frame to convert I{to} (L{RefFrame}).
|
|
179
|
+
@kwarg reframe: Optional reference frame to convert I{from} (L{RefFrame}),
|
|
180
|
+
overriding this point's reference frame.
|
|
181
|
+
@kwarg epoch: Optional epoch (L{Epoch}, C{scalar} or C{str}), overriding
|
|
182
|
+
this point's C{epoch or B{reframe}.epoch}.
|
|
183
|
+
@kwarg epoch2: Optional epoch to observe for the converted point (L{Epoch},
|
|
184
|
+
C{scalar} or C{str}), otherwise B{C{epoch}}.
|
|
185
|
+
@kwarg name: Optional name (C{str}), C{B{reframe2}.name} iff converted.
|
|
186
|
+
|
|
187
|
+
@return: The converted point (ellipsoidal C{Cartesian}) or if conversion
|
|
188
|
+
C{isunity}, this point or a copy of this point if the B{C{name}}
|
|
189
|
+
is non-empty.
|
|
190
|
+
|
|
191
|
+
@raise TRFError: This point's C{reframe} is not defined, invalid B{C{epoch}}
|
|
192
|
+
or B{C{epoch2}} or conversion from this point's C{reframe}
|
|
193
|
+
to B{C{reframe2}} is not available.
|
|
194
|
+
|
|
195
|
+
@raise TypeError: B{C{reframe2}} or B{C{reframe}} not a L{RefFrame}.
|
|
196
|
+
'''
|
|
197
|
+
return _MODS.trf._toRefFrame(self, reframe2, reframe=reframe, epoch=epoch,
|
|
198
|
+
epoch2=epoch2, name=name)
|
|
199
|
+
|
|
200
|
+
@deprecated_method
|
|
201
|
+
def toTransforms_(self, *transforms, **datum): # PYCHOK no cover
|
|
202
|
+
'''DEPRECATED on 2024.02.14, use method C{toTransform}.'''
|
|
203
|
+
r = self
|
|
204
|
+
for t in transforms:
|
|
205
|
+
r = r.toTransform(t)
|
|
206
|
+
return r.dup(**datum) if datum else r
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class LatLonEllipsoidalBase(LatLonBase):
|
|
210
|
+
'''(INTERNAL) Base class for ellipsoidal C{LatLon}s.
|
|
211
|
+
'''
|
|
212
|
+
_datum = _WGS84 # L{Datum}
|
|
213
|
+
_elevation2to = None # _elevation2 timeout (C{secs})
|
|
214
|
+
_epoch = None # overriding .reframe.epoch (C{float})
|
|
215
|
+
_gamma = None # UTM/UPS meridian convergence (C{degrees})
|
|
216
|
+
_geoidHeight2to = None # _geoidHeight2 timeout (C{secs})
|
|
217
|
+
_reframe = None # reference frame (L{RefFrame})
|
|
218
|
+
_scale = None # UTM/UPS scale factor (C{float})
|
|
219
|
+
_toLLEB_args = () # Etm/Utm/Ups._toLLEB arguments
|
|
220
|
+
|
|
221
|
+
def __init__(self, latlonh, lon=None, height=0, datum=None, reframe=None,
|
|
222
|
+
epoch=None, wrap=False, name=NN):
|
|
223
|
+
'''Create an ellipsoidal C{LatLon} point from the given lat-, longitude
|
|
224
|
+
and height on the given datum, reference frame and epoch.
|
|
225
|
+
|
|
226
|
+
@arg latlonh: Latitude (C{degrees} or DMS C{str} with N or S suffix) or
|
|
227
|
+
a previous C{LatLon} instance provided C{B{lon}=None}.
|
|
228
|
+
@kwarg lon: Longitude (C{degrees} or DMS C{str} with E or W suffix) or
|
|
229
|
+
C(None), indicating B{C{latlonh}} is a C{LatLon}.
|
|
230
|
+
@kwarg height: Optional height above (or below) the earth surface
|
|
231
|
+
(C{meter}, same units as the datum's ellipsoid axes).
|
|
232
|
+
@kwarg datum: Optional, ellipsoidal datum to use (L{Datum}, L{Ellipsoid},
|
|
233
|
+
L{Ellipsoid2} or L{a_f2Tuple}).
|
|
234
|
+
@kwarg reframe: Optional reference frame (L{RefFrame}).
|
|
235
|
+
@kwarg epoch: Optional epoch to observe for B{C{reframe}} (C{scalar}),
|
|
236
|
+
a non-zero, fractional calendar year; silently ignored
|
|
237
|
+
if C{B{reframe}=None}.
|
|
238
|
+
@kwarg wrap: If C{True}, wrap or I{normalize} B{C{lat}} and B{C{lon}}
|
|
239
|
+
(C{bool}).
|
|
240
|
+
@kwarg name: Optional name (C{str}).
|
|
241
|
+
|
|
242
|
+
@raise RangeError: Value of C{lat} or B{C{lon}} outside the valid
|
|
243
|
+
range and L{rangerrors} set to C{True}.
|
|
244
|
+
|
|
245
|
+
@raise TypeError: If B{C{latlonh}} is not a C{LatLon}, B{C{datum}} is
|
|
246
|
+
not a L{Datum}, B{C{reframe}} is not a L{RefFrame}
|
|
247
|
+
or B{C{epoch}} is not C{scalar} non-zero.
|
|
248
|
+
|
|
249
|
+
@raise UnitError: Invalid B{C{lat}}, B{C{lon}} or B{C{height}}.
|
|
250
|
+
'''
|
|
251
|
+
LatLonBase.__init__(self, latlonh, lon=lon, height=height, wrap=wrap, name=name)
|
|
252
|
+
if datum not in (None, self._datum, _EWGS84):
|
|
253
|
+
self.datum = _ellipsoidal_datum(datum, name=name)
|
|
254
|
+
if reframe:
|
|
255
|
+
self.reframe = reframe
|
|
256
|
+
self.epoch = epoch
|
|
257
|
+
|
|
258
|
+
# def __matmul__(self, other): # PYCHOK Python 3.5+
|
|
259
|
+
# '''Return C{NotImplemented} for C{ll_ = ll @ datum} and C{ll_ = ll @ reframe}.
|
|
260
|
+
# '''
|
|
261
|
+
# RefFrame = _MODS.trf.RefFrame
|
|
262
|
+
# return NotImplemented if isinstance(other, (Datum, RefFrame)) else \
|
|
263
|
+
# _NotImplemented(self, other)
|
|
264
|
+
|
|
265
|
+
def antipode(self, height=None):
|
|
266
|
+
'''Return the antipode, the point diametrically opposite
|
|
267
|
+
to this point.
|
|
268
|
+
|
|
269
|
+
@kwarg height: Optional height of the antipode, height
|
|
270
|
+
of this point otherwise (C{meter}).
|
|
271
|
+
|
|
272
|
+
@return: The antipodal point (C{LatLon}).
|
|
273
|
+
'''
|
|
274
|
+
lla = LatLonBase.antipode(self, height=height)
|
|
275
|
+
if lla.datum != self.datum:
|
|
276
|
+
lla.datum = self.datum
|
|
277
|
+
return lla
|
|
278
|
+
|
|
279
|
+
@deprecated_property_RO
|
|
280
|
+
def convergence(self):
|
|
281
|
+
'''DEPRECATED, use property C{gamma}.'''
|
|
282
|
+
return self.gamma
|
|
283
|
+
|
|
284
|
+
@deprecated_method
|
|
285
|
+
def convertDatum(self, datum2):
|
|
286
|
+
'''DEPRECATED, use method L{toDatum}.'''
|
|
287
|
+
return self.toDatum(datum2)
|
|
288
|
+
|
|
289
|
+
@deprecated_method
|
|
290
|
+
def convertRefFrame(self, reframe2):
|
|
291
|
+
'''DEPRECATED, use method L{toRefFrame}.'''
|
|
292
|
+
return self.toRefFrame(reframe2)
|
|
293
|
+
|
|
294
|
+
@Property_RO
|
|
295
|
+
def _css(self):
|
|
296
|
+
'''(INTERNAL) Get this C{LatLon} point as a Cassini-Soldner location (L{Css}).
|
|
297
|
+
'''
|
|
298
|
+
css = _MODS.css
|
|
299
|
+
return css.toCss(self, height=self.height, Css=css.Css, name=self.name)
|
|
300
|
+
|
|
301
|
+
@property_doc_(''' this points's datum (L{Datum}).''')
|
|
302
|
+
def datum(self):
|
|
303
|
+
'''Get this point's datum (L{Datum}).
|
|
304
|
+
'''
|
|
305
|
+
return self._datum
|
|
306
|
+
|
|
307
|
+
@datum.setter # PYCHOK setter!
|
|
308
|
+
def datum(self, datum):
|
|
309
|
+
'''Set this point's datum I{without conversion} (L{Datum}).
|
|
310
|
+
|
|
311
|
+
@raise TypeError: The B{C{datum}} is not a L{Datum}
|
|
312
|
+
or not ellipsoidal.
|
|
313
|
+
'''
|
|
314
|
+
_xinstanceof(Datum, datum=datum)
|
|
315
|
+
if not datum.isEllipsoidal:
|
|
316
|
+
raise _IsnotError(_ellipsoidal_, datum=datum)
|
|
317
|
+
if self._datum != datum:
|
|
318
|
+
_update_all(self)
|
|
319
|
+
self._datum = datum
|
|
320
|
+
|
|
321
|
+
def distanceTo2(self, other, wrap=False):
|
|
322
|
+
'''I{Approximate} the distance and (initial) bearing between this
|
|
323
|
+
and an other (ellipsoidal) point based on the radii of curvature.
|
|
324
|
+
|
|
325
|
+
I{Suitable only for short distances up to a few hundred Km
|
|
326
|
+
or Miles and only between points not near-polar}.
|
|
327
|
+
|
|
328
|
+
@arg other: The other point (C{LatLon}).
|
|
329
|
+
@kwarg wrap: If C{True}, wrap or I{normalize} the B{C{other}}
|
|
330
|
+
point (C{bool}).
|
|
331
|
+
|
|
332
|
+
@return: An L{Distance2Tuple}C{(distance, initial)}.
|
|
333
|
+
|
|
334
|
+
@raise TypeError: The B{C{other}} point is not C{LatLon}.
|
|
335
|
+
|
|
336
|
+
@raise ValueError: Incompatible datum ellipsoids.
|
|
337
|
+
|
|
338
|
+
@see: Method L{Ellipsoid.distance2} and U{Local, flat earth
|
|
339
|
+
approximation<https://www.EdWilliams.org/avform.htm#flat>}
|
|
340
|
+
aka U{Hubeny<https://www.OVG.AT/de/vgi/files/pdf/3781/>}
|
|
341
|
+
formula.
|
|
342
|
+
'''
|
|
343
|
+
p = self.others(other)
|
|
344
|
+
if wrap:
|
|
345
|
+
p = _Wrap.point(p)
|
|
346
|
+
E = self.ellipsoids(other)
|
|
347
|
+
return E.distance2(*(self.latlon + p.latlon))
|
|
348
|
+
|
|
349
|
+
@Property_RO
|
|
350
|
+
def _elevation2(self):
|
|
351
|
+
'''(INTERNAL) Get elevation and data source.
|
|
352
|
+
'''
|
|
353
|
+
return _MODS.elevations.elevation2(self.lat, self.lon,
|
|
354
|
+
timeout=self._elevation2to)
|
|
355
|
+
|
|
356
|
+
def elevation2(self, adjust=True, datum=None, timeout=2):
|
|
357
|
+
'''Return elevation of this point for its or the given datum, ellipsoid
|
|
358
|
+
or sphere.
|
|
359
|
+
|
|
360
|
+
@kwarg adjust: Adjust the elevation for a B{C{datum}} other than
|
|
361
|
+
C{NAD83} (C{bool}).
|
|
362
|
+
@kwarg datum: Optional datum overriding this point's datum (L{Datum},
|
|
363
|
+
L{Ellipsoid}, L{Ellipsoid2}, L{a_f2Tuple} or C{scalar}
|
|
364
|
+
radius).
|
|
365
|
+
@kwarg timeout: Optional query timeout (C{seconds}).
|
|
366
|
+
|
|
367
|
+
@return: An L{Elevation2Tuple}C{(elevation, data_source)} or
|
|
368
|
+
C{(None, error)} in case of errors.
|
|
369
|
+
|
|
370
|
+
@note: The adjustment applied is the difference in geocentric earth
|
|
371
|
+
radius between the B{C{datum}} and C{NAV83} upon which the
|
|
372
|
+
L{elevations.elevation2} is based.
|
|
373
|
+
|
|
374
|
+
@note: NED elevation is only available for locations within the U{Conterminous
|
|
375
|
+
US (CONUS)<https://WikiPedia.org/wiki/Contiguous_United_States>}.
|
|
376
|
+
|
|
377
|
+
@see: Function L{elevations.elevation2} and method C{Ellipsoid.Rgeocentric}
|
|
378
|
+
for further details and possible C{error}s.
|
|
379
|
+
'''
|
|
380
|
+
if self._elevation2to != timeout:
|
|
381
|
+
self._elevation2to = timeout
|
|
382
|
+
LatLonEllipsoidalBase._elevation2._update(self)
|
|
383
|
+
return self._Radjust2(adjust, datum, self._elevation2)
|
|
384
|
+
|
|
385
|
+
def ellipsoid(self, datum=_WGS84):
|
|
386
|
+
'''Return the ellipsoid of this point's datum or the given datum.
|
|
387
|
+
|
|
388
|
+
@kwarg datum: Default datum (L{Datum}).
|
|
389
|
+
|
|
390
|
+
@return: The ellipsoid (L{Ellipsoid} or L{Ellipsoid2}).
|
|
391
|
+
'''
|
|
392
|
+
return _xattr(self, datum=datum).ellipsoid
|
|
393
|
+
|
|
394
|
+
@property_RO
|
|
395
|
+
def ellipsoidalLatLon(self):
|
|
396
|
+
'''Get this C{LatLon}'s ellipsoidal class.
|
|
397
|
+
'''
|
|
398
|
+
return type(self)
|
|
399
|
+
|
|
400
|
+
def ellipsoids(self, other):
|
|
401
|
+
'''Check the type and ellipsoid of this and an other point's datum.
|
|
402
|
+
|
|
403
|
+
@arg other: The other point (C{LatLon}).
|
|
404
|
+
|
|
405
|
+
@return: This point's datum ellipsoid (L{Ellipsoid} or L{Ellipsoid2}).
|
|
406
|
+
|
|
407
|
+
@raise TypeError: The B{C{other}} point is not C{LatLon}.
|
|
408
|
+
|
|
409
|
+
@raise ValueError: Incompatible datum ellipsoids.
|
|
410
|
+
'''
|
|
411
|
+
self.others(other, up=2) # ellipsoids' caller
|
|
412
|
+
|
|
413
|
+
E = self.ellipsoid()
|
|
414
|
+
try: # other may be Sphere, etc.
|
|
415
|
+
e = other.ellipsoid()
|
|
416
|
+
except AttributeError:
|
|
417
|
+
try: # no ellipsoid method, try datum
|
|
418
|
+
e = other.datum.ellipsoid
|
|
419
|
+
except AttributeError:
|
|
420
|
+
e = E # no datum, XXX assume equivalent?
|
|
421
|
+
if e != E:
|
|
422
|
+
raise _ValueError(e.named2, txt=_incompatible(E.named2))
|
|
423
|
+
return E
|
|
424
|
+
|
|
425
|
+
@property_doc_(''' this point's observed or C{reframe} epoch (C{float}).''')
|
|
426
|
+
def epoch(self):
|
|
427
|
+
'''Get this point's observed or C{reframe} epoch (L{Epoch}) or C{None}.
|
|
428
|
+
'''
|
|
429
|
+
return self._epoch or (self.reframe.epoch if self.reframe else None)
|
|
430
|
+
|
|
431
|
+
@epoch.setter # PYCHOK setter!
|
|
432
|
+
def epoch(self, epoch):
|
|
433
|
+
'''Set or clear this point's observed epoch, a fractional
|
|
434
|
+
calendar year (L{Epoch}, C{scalar} or C{str}) or C{None}.
|
|
435
|
+
|
|
436
|
+
@raise TRFError: Invalid B{C{epoch}}.
|
|
437
|
+
'''
|
|
438
|
+
self._epoch = None if epoch is None else Epoch(epoch)
|
|
439
|
+
|
|
440
|
+
@Property_RO
|
|
441
|
+
def Equidistant(self):
|
|
442
|
+
'''Get the prefered azimuthal equidistant projection I{class} (L{EquidistantKarney} or L{EquidistantExact}).
|
|
443
|
+
'''
|
|
444
|
+
try:
|
|
445
|
+
_ = self.datum.ellipsoid.geodesic
|
|
446
|
+
return _MODS.azimuthal.EquidistantKarney
|
|
447
|
+
except ImportError: # no geographiclib
|
|
448
|
+
return _MODS.azimuthal.EquidistantExact # XXX no longer L{azimuthal.Equidistant}
|
|
449
|
+
|
|
450
|
+
@Property_RO
|
|
451
|
+
def _etm(self):
|
|
452
|
+
'''(INTERNAL) Get this C{LatLon} point as an ETM coordinate (L{pygeodesy.toEtm8}).
|
|
453
|
+
'''
|
|
454
|
+
etm = _MODS.etm
|
|
455
|
+
return etm.toEtm8(self, datum=self.datum, Etm=etm.Etm)
|
|
456
|
+
|
|
457
|
+
@property_RO
|
|
458
|
+
def gamma(self):
|
|
459
|
+
'''Get this point's UTM or UPS meridian convergence (C{degrees}) or
|
|
460
|
+
C{None} if not available or not converted from L{Utm} or L{Ups}.
|
|
461
|
+
'''
|
|
462
|
+
return self._gamma
|
|
463
|
+
|
|
464
|
+
@Property_RO
|
|
465
|
+
def _geoidHeight2(self):
|
|
466
|
+
'''(INTERNAL) Get geoid height and model.
|
|
467
|
+
'''
|
|
468
|
+
return _MODS.elevations.geoidHeight2(self.lat, self.lon, model=0,
|
|
469
|
+
timeout=self._geoidHeight2to)
|
|
470
|
+
|
|
471
|
+
def geoidHeight2(self, adjust=False, datum=None, timeout=2):
|
|
472
|
+
'''Return geoid height of this point for its or the given datum, ellipsoid
|
|
473
|
+
or sphere.
|
|
474
|
+
|
|
475
|
+
@kwarg adjust: Adjust the geoid height for a B{C{datum}} other than
|
|
476
|
+
C{NAD83/NADV88} (C{bool}).
|
|
477
|
+
@kwarg datum: Optional datum overriding this point's datum (L{Datum},
|
|
478
|
+
L{Ellipsoid}, L{Ellipsoid2}, L{a_f2Tuple} or C{scalar}
|
|
479
|
+
radius).
|
|
480
|
+
@kwarg timeout: Optional query timeout (C{seconds}).
|
|
481
|
+
|
|
482
|
+
@return: A L{GeoidHeight2Tuple}C{(height, model_name)} or
|
|
483
|
+
C{(None, error)} in case of errors.
|
|
484
|
+
|
|
485
|
+
@note: The adjustment applied is the difference in geocentric earth
|
|
486
|
+
radius between the B{C{datum}} and C{NAV83/NADV88} upon which
|
|
487
|
+
the L{elevations.geoidHeight2} is based.
|
|
488
|
+
|
|
489
|
+
@note: The geoid height is only available for locations within the U{Conterminous
|
|
490
|
+
US (CONUS)<https://WikiPedia.org/wiki/Contiguous_United_States>}.
|
|
491
|
+
|
|
492
|
+
@see: Function L{elevations.geoidHeight2} and method C{Ellipsoid.Rgeocentric}
|
|
493
|
+
for further details and possible C{error}s.
|
|
494
|
+
'''
|
|
495
|
+
if self._geoidHeight2to != timeout:
|
|
496
|
+
self._geoidHeight2to = timeout
|
|
497
|
+
LatLonEllipsoidalBase._geoidHeight2._update(self)
|
|
498
|
+
return self._Radjust2(adjust, datum, self._geoidHeight2)
|
|
499
|
+
|
|
500
|
+
def intermediateTo(self, other, fraction, height=None, wrap=False): # PYCHOK no cover
|
|
501
|
+
'''I{Must be overloaded}.'''
|
|
502
|
+
_MODS.named.notOverloaded(self, other, fraction, height=height, wrap=wrap)
|
|
503
|
+
|
|
504
|
+
def intersection3(self, end1, other, end2, height=None, wrap=False, # was=True
|
|
505
|
+
equidistant=None, tol=_TOL_M):
|
|
506
|
+
'''I{Iteratively} compute the intersection point of two lines, each
|
|
507
|
+
defined by two points or a start point and bearing from North.
|
|
508
|
+
|
|
509
|
+
@arg end1: End point of this line (C{LatLon}) or the initial
|
|
510
|
+
bearing at this point (compass C{degrees360}).
|
|
511
|
+
@arg other: Start point of the other line (C{LatLon}).
|
|
512
|
+
@arg end2: End point of the other line (C{LatLon}) or the initial
|
|
513
|
+
bearing at the other point (compass C{degrees360}).
|
|
514
|
+
@kwarg height: Optional height at the intersection (C{meter},
|
|
515
|
+
conventionally) or C{None} for the mean height.
|
|
516
|
+
@kwarg wrap: If C{True}, wrap or I{normalize} and unroll the
|
|
517
|
+
B{C{other}} and B{C{end*}} points (C{bool}).
|
|
518
|
+
@kwarg equidistant: An azimuthal equidistant projection (I{class} or
|
|
519
|
+
function L{pygeodesy.equidistant}), or C{None}
|
|
520
|
+
for this point's preferred C{.Equidistant}.
|
|
521
|
+
@kwarg tol: Tolerance for convergence and skew line distance and
|
|
522
|
+
length (C{meter}, conventionally).
|
|
523
|
+
|
|
524
|
+
@return: An L{Intersection3Tuple}C{(point, outside1, outside2)}
|
|
525
|
+
with C{point} a C{LatLon} instance.
|
|
526
|
+
|
|
527
|
+
@raise ImportError: Package U{geographiclib
|
|
528
|
+
<https://PyPI.org/project/geographiclib>}
|
|
529
|
+
not installed or not found, but only if
|
|
530
|
+
C{B{equidistant}=}L{EquidistantKarney}.
|
|
531
|
+
|
|
532
|
+
@raise IntersectionError: Skew, colinear, parallel or otherwise
|
|
533
|
+
non-intersecting lines or no convergence
|
|
534
|
+
for the given B{C{tol}}.
|
|
535
|
+
|
|
536
|
+
@raise TypeError: If B{C{end1}}, B{C{other}} or B{C{end2}} point
|
|
537
|
+
is not C{LatLon}.
|
|
538
|
+
|
|
539
|
+
@note: For each line specified with an initial bearing, a pseudo-end
|
|
540
|
+
point is computed as the C{destination} along that bearing at
|
|
541
|
+
about 1.5 times the distance from the start point to an initial
|
|
542
|
+
gu-/estimate of the intersection point (and between 1/8 and 3/8
|
|
543
|
+
of the authalic earth perimeter).
|
|
544
|
+
|
|
545
|
+
@see: I{Karney's} U{intersect.cpp<https://SourceForge.net/p/geographiclib/
|
|
546
|
+
discussion/1026621/thread/21aaff9f/>}, U{The B{ellipsoidal} case<https://
|
|
547
|
+
GIS.StackExchange.com/questions/48937/calculating-intersection-of-two-circles>}
|
|
548
|
+
and U{Karney's paper<https://ArXiv.org/pdf/1102.1215.pdf>}, pp 20-21, section
|
|
549
|
+
B{14. MARITIME BOUNDARIES} for more details about the iteration algorithm.
|
|
550
|
+
'''
|
|
551
|
+
try:
|
|
552
|
+
s2 = self.others(other)
|
|
553
|
+
return _MODS.ellipsoidalBaseDI._intersect3(self, end1,
|
|
554
|
+
s2, end2,
|
|
555
|
+
height=height, wrap=wrap,
|
|
556
|
+
equidistant=equidistant, tol=tol,
|
|
557
|
+
LatLon=self.classof, datum=self.datum)
|
|
558
|
+
except (TypeError, ValueError) as x:
|
|
559
|
+
raise _xError(x, start1=self, end1=end1, other=other, end2=end2,
|
|
560
|
+
height=height, wrap=wrap, tol=tol)
|
|
561
|
+
|
|
562
|
+
def intersections2(self, radius1, other, radius2, height=None, wrap=False, # was=True
|
|
563
|
+
equidistant=None, tol=_TOL_M):
|
|
564
|
+
'''I{Iteratively} compute the intersection points of two circles,
|
|
565
|
+
each defined by a center point and a radius.
|
|
566
|
+
|
|
567
|
+
@arg radius1: Radius of this circle (C{meter}, conventionally).
|
|
568
|
+
@arg other: Center of the other circle (C{LatLon}).
|
|
569
|
+
@arg radius2: Radius of the other circle (C{meter}, same units as
|
|
570
|
+
B{C{radius1}}).
|
|
571
|
+
@kwarg height: Optional height for the intersection points (C{meter},
|
|
572
|
+
conventionally) or C{None} for the I{"radical height"}
|
|
573
|
+
at the I{radical line} between both centers.
|
|
574
|
+
@kwarg wrap: If C{True}, wrap or I{normalize} and unroll the B{C{other}}
|
|
575
|
+
center (C{bool}).
|
|
576
|
+
@kwarg equidistant: An azimuthal equidistant projection (I{class} or
|
|
577
|
+
function L{pygeodesy.equidistant}) or C{None}
|
|
578
|
+
for this point's preferred C{.Equidistant}.
|
|
579
|
+
@kwarg tol: Convergence tolerance (C{meter}, same units as
|
|
580
|
+
B{C{radius1}} and B{C{radius2}}).
|
|
581
|
+
|
|
582
|
+
@return: 2-Tuple of the intersection points, each a C{LatLon}
|
|
583
|
+
instance. For abutting circles, both intersection
|
|
584
|
+
points are the same instance, aka the I{radical center}.
|
|
585
|
+
|
|
586
|
+
@raise ImportError: Package U{geographiclib
|
|
587
|
+
<https://PyPI.org/project/geographiclib>}
|
|
588
|
+
not installed or not found, but only if
|
|
589
|
+
C{B{equidistant}=}L{EquidistantKarney}.
|
|
590
|
+
|
|
591
|
+
@raise IntersectionError: Concentric, antipodal, invalid or
|
|
592
|
+
non-intersecting circles or no
|
|
593
|
+
convergence for the given B{C{tol}}.
|
|
594
|
+
|
|
595
|
+
@raise TypeError: Invalid B{C{other}} or B{C{equidistant}}.
|
|
596
|
+
|
|
597
|
+
@raise UnitError: Invalid B{C{radius1}}, B{C{radius2}} or B{C{height}}.
|
|
598
|
+
|
|
599
|
+
@see: U{The B{ellipsoidal} case<https://GIS.StackExchange.com/questions/48937/
|
|
600
|
+
calculating-intersection-of-two-circles>}, U{Karney's paper
|
|
601
|
+
<https://ArXiv.org/pdf/1102.1215.pdf>}, pp 20-21, section B{14. MARITIME BOUNDARIES},
|
|
602
|
+
U{circle-circle<https://MathWorld.Wolfram.com/Circle-CircleIntersection.html>} and
|
|
603
|
+
U{sphere-sphere<https://MathWorld.Wolfram.com/Sphere-SphereIntersection.html>}
|
|
604
|
+
intersections.
|
|
605
|
+
'''
|
|
606
|
+
try:
|
|
607
|
+
c2 = self.others(other)
|
|
608
|
+
return _MODS.ellipsoidalBaseDI._intersections2(self, radius1,
|
|
609
|
+
c2, radius2,
|
|
610
|
+
height=height, wrap=wrap,
|
|
611
|
+
equidistant=equidistant, tol=tol,
|
|
612
|
+
LatLon=self.classof, datum=self.datum)
|
|
613
|
+
except (AssertionError, TypeError, ValueError) as x:
|
|
614
|
+
raise _xError(x, center=self, radius1=radius1, other=other, radius2=radius2,
|
|
615
|
+
height=height, wrap=wrap, tol=tol)
|
|
616
|
+
|
|
617
|
+
def isenclosedBy(self, points, wrap=False):
|
|
618
|
+
'''Check whether a polygon or composite encloses this point.
|
|
619
|
+
|
|
620
|
+
@arg points: The polygon points or clips (C{LatLon}[],
|
|
621
|
+
L{BooleanFHP} or L{BooleanGH}).
|
|
622
|
+
@kwarg wrap: If C{True}, wrap or I{normalize} and unroll the
|
|
623
|
+
B{C{points}} (C{bool}).
|
|
624
|
+
|
|
625
|
+
@return: C{True} if this point is inside the polygon or composite,
|
|
626
|
+
C{False} otherwise.
|
|
627
|
+
|
|
628
|
+
@raise PointsError: Insufficient number of B{C{points}}.
|
|
629
|
+
|
|
630
|
+
@raise TypeError: Some B{C{points}} are not C{LatLon}.
|
|
631
|
+
|
|
632
|
+
@raise ValueError: Invalid B{C{point}}, lat- or longitude.
|
|
633
|
+
|
|
634
|
+
@see: Functions L{pygeodesy.isconvex}, L{pygeodesy.isenclosedBy}
|
|
635
|
+
and L{pygeodesy.ispolar} especially if the B{C{points}} may
|
|
636
|
+
enclose a pole or wrap around the earth I{longitudinally}.
|
|
637
|
+
'''
|
|
638
|
+
return _MODS.points.isenclosedBy(self, points, wrap=wrap)
|
|
639
|
+
|
|
640
|
+
@property_RO
|
|
641
|
+
def iteration(self):
|
|
642
|
+
'''Get the most recent C{intersections2} or C{nearestOn} iteration
|
|
643
|
+
number (C{int}) or C{None} if not available/applicable.
|
|
644
|
+
'''
|
|
645
|
+
return self._iteration
|
|
646
|
+
|
|
647
|
+
@Property_RO
|
|
648
|
+
def _lcc(self):
|
|
649
|
+
'''(INTERNAL) Get this C{LatLon} point as a Lambert location (L{Lcc}).
|
|
650
|
+
'''
|
|
651
|
+
lcc = _MODS.lcc
|
|
652
|
+
return lcc.toLcc(self, height=self.height, Lcc=lcc.Lcc, name=self.name)
|
|
653
|
+
|
|
654
|
+
def midpointTo(self, other, height=None, fraction=_0_5, wrap=False):
|
|
655
|
+
'''Find the midpoint on a geodesic between this and an other point.
|
|
656
|
+
|
|
657
|
+
@arg other: The other point (C{LatLon}).
|
|
658
|
+
@kwarg height: Optional height for midpoint, overriding the
|
|
659
|
+
mean height (C{meter}).
|
|
660
|
+
@kwarg fraction: Midpoint location from this point (C{scalar}),
|
|
661
|
+
may be negative or greater than 1.0.
|
|
662
|
+
@kwarg wrap: If C{True}, wrap or I{normalize} and unroll the
|
|
663
|
+
B{C{other}} point (C{bool}).
|
|
664
|
+
|
|
665
|
+
@return: Midpoint (C{LatLon}).
|
|
666
|
+
|
|
667
|
+
@raise TypeError: The B{C{other}} point is not C{LatLon}.
|
|
668
|
+
|
|
669
|
+
@raise ValueError: Invalid B{C{height}}.
|
|
670
|
+
|
|
671
|
+
@see: Methods C{intermediateTo} and C{rhumbMidpointTo}.
|
|
672
|
+
'''
|
|
673
|
+
return self.intermediateTo(other, fraction, height=height, wrap=wrap)
|
|
674
|
+
|
|
675
|
+
def nearestOn(self, point1, point2, within=True, height=None, wrap=False, # was=True
|
|
676
|
+
equidistant=None, tol=_TOL_M):
|
|
677
|
+
'''I{Iteratively} locate the closest point on the geodesic between
|
|
678
|
+
two other (ellipsoidal) points.
|
|
679
|
+
|
|
680
|
+
@arg point1: Start point (C{LatLon}).
|
|
681
|
+
@arg point2: End point (C{LatLon}).
|
|
682
|
+
@kwarg within: If C{True} return the closest point I{between}
|
|
683
|
+
B{C{point1}} and B{C{point2}}, otherwise the
|
|
684
|
+
closest point elsewhere on the geodesic (C{bool}).
|
|
685
|
+
@kwarg height: Optional height for the closest point (C{meter},
|
|
686
|
+
conventionally) or C{None} or C{False} for the
|
|
687
|
+
interpolated height. If C{False}, the closest
|
|
688
|
+
takes the heights of the points into account.
|
|
689
|
+
@kwarg wrap: If C{True}, wrap or I{normalize} and unroll both
|
|
690
|
+
B{C{point1}} and B{C{point2}} (C{bool}).
|
|
691
|
+
@kwarg equidistant: An azimuthal equidistant projection (I{class} or
|
|
692
|
+
function L{pygeodesy.equidistant}) or C{None}
|
|
693
|
+
for this point's preferred C{.Equidistant}.
|
|
694
|
+
@kwarg tol: Convergence tolerance (C{meter}, conventionally).
|
|
695
|
+
|
|
696
|
+
@return: Closest point (C{LatLon}).
|
|
697
|
+
|
|
698
|
+
@raise ImportError: Package U{geographiclib
|
|
699
|
+
<https://PyPI.org/project/geographiclib>}
|
|
700
|
+
not installed or not found, but only if
|
|
701
|
+
C{B{equidistant}=}L{EquidistantKarney}.
|
|
702
|
+
|
|
703
|
+
@raise TypeError: Invalid B{C{point1}}, B{C{point2}} or
|
|
704
|
+
B{C{equidistant}}.
|
|
705
|
+
|
|
706
|
+
@raise ValueError: Datum or ellipsoid of B{C{point1}} or B{C{point2}} is
|
|
707
|
+
incompatible or no convergence for the given B{C{tol}}.
|
|
708
|
+
|
|
709
|
+
@see: I{Karney}'s U{intercept.cpp<https://SourceForge.net/p/geographiclib/
|
|
710
|
+
discussion/1026621/thread/21aaff9f/>}, U{The B{ellipsoidal} case<https://
|
|
711
|
+
GIS.StackExchange.com/questions/48937/calculating-intersection-of-two-circles>}
|
|
712
|
+
and U{Karney's paper<https://ArXiv.org/pdf/1102.1215.pdf>}, pp 20-21, section
|
|
713
|
+
B{14. MARITIME BOUNDARIES} for details about the iteration algorithm.
|
|
714
|
+
'''
|
|
715
|
+
try:
|
|
716
|
+
t = _MODS.ellipsoidalBaseDI._nearestOn2(self, point1, point2, within=within,
|
|
717
|
+
height=height, wrap=wrap,
|
|
718
|
+
equidistant=equidistant,
|
|
719
|
+
tol=tol, LatLon=self.classof)
|
|
720
|
+
except (TypeError, ValueError) as x:
|
|
721
|
+
raise _xError(x, point=self, point1=point1, point2=point2, within=within,
|
|
722
|
+
height=height, wrap=wrap, tol=tol)
|
|
723
|
+
return t.closest
|
|
724
|
+
|
|
725
|
+
@Property_RO
|
|
726
|
+
def _osgr(self):
|
|
727
|
+
'''(INTERNAL) Get this C{LatLon} point as an OSGR coordinate (L{Osgr}),
|
|
728
|
+
based on the OS recommendation.
|
|
729
|
+
'''
|
|
730
|
+
return _MODS.osgr.toOsgr(self, kTM=False, name=self.name) # datum=self.datum
|
|
731
|
+
|
|
732
|
+
@Property_RO
|
|
733
|
+
def _osgrTM(self):
|
|
734
|
+
'''(INTERNAL) Get this C{LatLon} point as an OSGR coordinate (L{Osgr})
|
|
735
|
+
based on I{Karney}'s Krüger implementation.
|
|
736
|
+
'''
|
|
737
|
+
return _MODS.osgr.toOsgr(self, kTM=True, name=self.name) # datum=self.datum
|
|
738
|
+
|
|
739
|
+
def parse(self, strllh, height=0, datum=None, epoch=None, reframe=None,
|
|
740
|
+
sep=_COMMA_, wrap=False, name=NN):
|
|
741
|
+
'''Parse a string consisting of C{"lat, lon[, height]"},
|
|
742
|
+
representing a similar, ellipsoidal C{LatLon} point.
|
|
743
|
+
|
|
744
|
+
@arg strllh: Lat, lon and optional height (C{str}),
|
|
745
|
+
see function L{pygeodesy.parse3llh}.
|
|
746
|
+
@kwarg height: Optional, default height (C{meter} or
|
|
747
|
+
C{None}).
|
|
748
|
+
@kwarg datum: Optional datum (L{Datum}), overriding this
|
|
749
|
+
datum I{without conversion}.
|
|
750
|
+
@kwarg epoch: Optional datum (L{Epoch}), overriding this
|
|
751
|
+
epoch I{without conversion}.
|
|
752
|
+
@kwarg reframe: Optional datum (L{RefFrame}), overriding
|
|
753
|
+
this reframe I{without conversion}.
|
|
754
|
+
@kwarg sep: Optional separator (C{str}).
|
|
755
|
+
@kwarg wrap: If C{True}, wrap or I{normalize} the lat-
|
|
756
|
+
and longitude (C{bool}).
|
|
757
|
+
@kwarg name: Optional instance name (C{str}), overriding
|
|
758
|
+
this name.
|
|
759
|
+
|
|
760
|
+
@return: The similar point (ellipsoidal C{LatLon}).
|
|
761
|
+
|
|
762
|
+
@raise ParseError: Invalid B{C{strllh}}.
|
|
763
|
+
'''
|
|
764
|
+
a, b, h = _MODS.dms.parse3llh(strllh, height=height, sep=sep, wrap=wrap)
|
|
765
|
+
r = self.classof(a, b, height=h, datum=self.datum)
|
|
766
|
+
if datum not in (None, self.datum):
|
|
767
|
+
r.datum = datum
|
|
768
|
+
if epoch not in (None, self.epoch):
|
|
769
|
+
r.epoch = epoch
|
|
770
|
+
if reframe not in (None, self.reframe):
|
|
771
|
+
r.reframe = reframe
|
|
772
|
+
return self._xnamed(r, name=name, force=True) if name else r
|
|
773
|
+
|
|
774
|
+
def _Radjust2(self, adjust, datum, meter_text2):
|
|
775
|
+
'''(INTERNAL) Adjust an C{elevation} or C{geoidHeight} with
|
|
776
|
+
difference in Gaussian radii of curvature of the given
|
|
777
|
+
datum and NAD83 ellipsoids at this point's latitude.
|
|
778
|
+
|
|
779
|
+
@note: This is an arbitrary, possibly incorrect adjustment.
|
|
780
|
+
'''
|
|
781
|
+
if adjust: # Elevation2Tuple or GeoidHeight2Tuple
|
|
782
|
+
m, t = meter_text2
|
|
783
|
+
if isinstance(m, float) and fabs(m) > EPS:
|
|
784
|
+
n = Datums.NAD83.ellipsoid.rocGauss(self.lat)
|
|
785
|
+
if n > EPS0:
|
|
786
|
+
# use ratio, datum and NAD83 units may differ
|
|
787
|
+
E = self.ellipsoid() if datum in (None, self.datum) else \
|
|
788
|
+
_earth_ellipsoid(datum)
|
|
789
|
+
r = E.rocGauss(self.lat)
|
|
790
|
+
if r > EPS0 and fabs(r - n) > EPS: # EPS1
|
|
791
|
+
m *= r / n
|
|
792
|
+
meter_text2 = meter_text2.classof(m, t)
|
|
793
|
+
return self._xnamed(meter_text2)
|
|
794
|
+
|
|
795
|
+
@property_doc_(''' this point's reference frame (L{RefFrame}).''')
|
|
796
|
+
def reframe(self):
|
|
797
|
+
'''Get this point's reference frame (L{RefFrame}) or C{None}.
|
|
798
|
+
'''
|
|
799
|
+
return self._reframe
|
|
800
|
+
|
|
801
|
+
@reframe.setter # PYCHOK setter!
|
|
802
|
+
def reframe(self, reframe):
|
|
803
|
+
'''Set or clear this point's reference frame (L{RefFrame}) or C{None}.
|
|
804
|
+
|
|
805
|
+
@raise TypeError: The B{C{reframe}} is not a L{RefFrame}.
|
|
806
|
+
'''
|
|
807
|
+
_set_reframe(self, reframe)
|
|
808
|
+
|
|
809
|
+
@Property_RO
|
|
810
|
+
def scale(self):
|
|
811
|
+
'''Get this point's UTM grid or UPS point scale factor (C{float})
|
|
812
|
+
or C{None} if not converted from L{Utm} or L{Ups}.
|
|
813
|
+
'''
|
|
814
|
+
return self._scale
|
|
815
|
+
|
|
816
|
+
def toCartesian(self, height=None, **Cartesian_and_kwds): # PYCHOK signature
|
|
817
|
+
'''Convert this point to cartesian, I{geocentric} coordinates,
|
|
818
|
+
also known as I{Earth-Centered, Earth-Fixed} (ECEF).
|
|
819
|
+
|
|
820
|
+
@see: Method L{toCartesian<latlonBase.LatLonBase.toCartesian>}
|
|
821
|
+
for further details.
|
|
822
|
+
'''
|
|
823
|
+
kwds = Cartesian_and_kwds
|
|
824
|
+
if kwds:
|
|
825
|
+
kwds = _xkwds(kwds, reframe=self.reframe, epoch=self.epoch)
|
|
826
|
+
return LatLonBase.toCartesian(self, height=height, **kwds)
|
|
827
|
+
|
|
828
|
+
def toCss(self, **toCss_kwds):
|
|
829
|
+
'''Convert this C{LatLon} point to a Cassini-Soldner location.
|
|
830
|
+
|
|
831
|
+
@kwarg toCss_kwds: Optional L{pygeodesy.toCss} keyword arguments.
|
|
832
|
+
|
|
833
|
+
@return: The Cassini-Soldner location (L{Css}).
|
|
834
|
+
|
|
835
|
+
@see: Function L{pygeodesy.toCss}.
|
|
836
|
+
'''
|
|
837
|
+
return self._css if not toCss_kwds else _MODS.css.toCss(
|
|
838
|
+
self, **_xkwds(toCss_kwds, name=self.name))
|
|
839
|
+
|
|
840
|
+
def toDatum(self, datum2, height=None, name=NN):
|
|
841
|
+
'''Convert this point to an other datum.
|
|
842
|
+
|
|
843
|
+
@arg datum2: Datum to convert I{to} (L{Datum}).
|
|
844
|
+
@kwarg height: Optional height, overriding the
|
|
845
|
+
converted height (C{meter}).
|
|
846
|
+
@kwarg name: Optional name (C{str}), iff converted.
|
|
847
|
+
|
|
848
|
+
@return: The converted point (ellipsoidal C{LatLon})
|
|
849
|
+
or a copy of this point if B{C{datum2}}
|
|
850
|
+
matches this point's C{datum}.
|
|
851
|
+
|
|
852
|
+
@raise TypeError: Invalid B{C{datum2}}.
|
|
853
|
+
'''
|
|
854
|
+
n = name or self.name
|
|
855
|
+
d2 = _ellipsoidal_datum(datum2, name=n)
|
|
856
|
+
if self.datum == d2:
|
|
857
|
+
r = self.copy(name=name)
|
|
858
|
+
else:
|
|
859
|
+
kwds = _xkwds_not(None, LatLon=self.classof, name=n,
|
|
860
|
+
epoch=self.epoch, reframe=self.reframe)
|
|
861
|
+
c = self.toCartesian().toDatum(d2)
|
|
862
|
+
r = c.toLatLon(datum=d2, height=height, **kwds)
|
|
863
|
+
return r
|
|
864
|
+
|
|
865
|
+
def toEtm(self, **toEtm8_kwds):
|
|
866
|
+
'''Convert this C{LatLon} point to an ETM coordinate.
|
|
867
|
+
|
|
868
|
+
@kwarg toEtm8_kwds: Optional L{pygeodesy.toEtm8} keyword arguments.
|
|
869
|
+
|
|
870
|
+
@return: The ETM coordinate (L{Etm}).
|
|
871
|
+
|
|
872
|
+
@see: Function L{pygeodesy.toEtm8}.
|
|
873
|
+
'''
|
|
874
|
+
return self._etm if not toEtm8_kwds else _MODS.etm.toEtm8(
|
|
875
|
+
self, **_xkwds(toEtm8_kwds, name=self.name))
|
|
876
|
+
|
|
877
|
+
def toLcc(self, **toLcc_kwds):
|
|
878
|
+
'''Convert this C{LatLon} point to a Lambert location.
|
|
879
|
+
|
|
880
|
+
@kwarg toLcc_kwds: Optional L{pygeodesy.toLcc} keyword arguments.
|
|
881
|
+
|
|
882
|
+
@return: The Lambert location (L{Lcc}).
|
|
883
|
+
|
|
884
|
+
@see: Function L{pygeodesy.toLcc}.
|
|
885
|
+
'''
|
|
886
|
+
return self._lcc if not toLcc_kwds else _MODS.lcc.toLcc(
|
|
887
|
+
self, **_xkwds(toLcc_kwds, name=self.name))
|
|
888
|
+
|
|
889
|
+
def toMgrs(self, center=False, pole=NN):
|
|
890
|
+
'''Convert this C{LatLon} point to an MGRS coordinate.
|
|
891
|
+
|
|
892
|
+
@kwarg center: If C{True}, try to I{un}-center MGRS
|
|
893
|
+
to its C{lowerleft} (C{bool}) or by
|
|
894
|
+
C{B{center} meter} (C{scalar}).
|
|
895
|
+
@kwarg pole: Optional top/center for the MGRS UPS
|
|
896
|
+
projection (C{str}, 'N[orth]' or 'S[outh]').
|
|
897
|
+
|
|
898
|
+
@return: The MGRS coordinate (L{Mgrs}).
|
|
899
|
+
|
|
900
|
+
@see: Method L{toUtmUps} and L{Mgrs.toLatLon}.
|
|
901
|
+
'''
|
|
902
|
+
return self.toUtmUps(center=center, pole=pole).toMgrs(center=False)
|
|
903
|
+
|
|
904
|
+
def toOsgr(self, kTM=False, **toOsgr_kwds):
|
|
905
|
+
'''Convert this C{LatLon} point to an OSGR coordinate.
|
|
906
|
+
|
|
907
|
+
@kwarg kTM: If C{True} use I{Karney}'s Krüger method from module
|
|
908
|
+
L{ktm}, otherwise I{Ordinance Survery}'s recommended
|
|
909
|
+
formulation (C{bool}).
|
|
910
|
+
@kwarg toOsgr_kwds: Optional L{pygeodesy.toOsgr} keyword arguments.
|
|
911
|
+
|
|
912
|
+
@return: The OSGR coordinate (L{Osgr}).
|
|
913
|
+
|
|
914
|
+
@see: Function L{pygeodesy.toOsgr}.
|
|
915
|
+
'''
|
|
916
|
+
if toOsgr_kwds:
|
|
917
|
+
r = _MODS.osgr.toOsgr(self, kTM=kTM, **_xkwds(toOsgr_kwds, name=self.name))
|
|
918
|
+
else:
|
|
919
|
+
r = self._osgrTM if kTM else self._osgr
|
|
920
|
+
return r
|
|
921
|
+
|
|
922
|
+
def toRefFrame(self, reframe2, reframe=None, epoch=None, epoch2=None, height=None, name=NN):
|
|
923
|
+
'''Convert this point to an other reference frame and epoch.
|
|
924
|
+
|
|
925
|
+
@arg reframe2: Reference frame to convert I{to} (L{RefFrame}).
|
|
926
|
+
@kwarg reframe: Optional reference frame to convert I{from} (L{RefFrame}),
|
|
927
|
+
overriding this point's reference frame.
|
|
928
|
+
@kwarg epoch: Optional epoch (L{Epoch}, C{scalar} or C{str}), overriding
|
|
929
|
+
this point's C{epoch or B{reframe}.epoch}.
|
|
930
|
+
@kwarg epoch2: Optional epoch to observe for the converted point (L{Epoch},
|
|
931
|
+
C{scalar} or C{str}), otherwise B{C{epoch}}.
|
|
932
|
+
@kwarg height: Optional height, overriding the converted height (C{meter}).
|
|
933
|
+
@kwarg name: Optional name (C{str}), C{B{reframe2}.name} iff converted.
|
|
934
|
+
|
|
935
|
+
@return: The converted point (ellipsoidal C{LatLon}) or if conversion
|
|
936
|
+
C{isunity}, this point or a copy of this point if the B{C{name}}
|
|
937
|
+
is non-empty.
|
|
938
|
+
|
|
939
|
+
@raise TRFError: This point's C{reframe} is not defined, invalid B{C{epoch}}
|
|
940
|
+
or B{C{epoch2}} or conversion from this point's C{reframe}
|
|
941
|
+
to B{C{reframe2}} is not available.
|
|
942
|
+
|
|
943
|
+
@raise TypeError: B{C{reframe2}} or B{C{reframe}} not a L{RefFrame}.
|
|
944
|
+
'''
|
|
945
|
+
return _MODS.trf._toRefFrame(self, reframe2, reframe=reframe, epoch=epoch,
|
|
946
|
+
epoch2=epoch2, name=name, height=height)
|
|
947
|
+
|
|
948
|
+
def toTransform(self, transform, inverse=False, datum=None, **LatLon_kwds):
|
|
949
|
+
'''Apply a Helmert transform to this geodetic point.
|
|
950
|
+
|
|
951
|
+
@arg transform: Transform to apply (L{Transform} or L{TransformXform}).
|
|
952
|
+
@kwarg inverse: Apply the inverse of the Helmert transform (C{bool}).
|
|
953
|
+
@kwarg datum: Datum for the transformed point (L{Datum}), overriding
|
|
954
|
+
this point's datum but I{not} taken it into account.
|
|
955
|
+
@kwarg LatLon_kwds: Optional keyword arguments for the transformed
|
|
956
|
+
point, like C{B{height}=...}.
|
|
957
|
+
|
|
958
|
+
@return: A transformed point (C{LatLon}) or a copy of this point if
|
|
959
|
+
C{B{transform}.isunity}.
|
|
960
|
+
|
|
961
|
+
@raise TypeError: Invalid B{C{transform}}.
|
|
962
|
+
'''
|
|
963
|
+
_xinstanceof(Transform, transform=transform)
|
|
964
|
+
d = datum or self.datum
|
|
965
|
+
if transform.isunity:
|
|
966
|
+
r = self.dup(datum=d, **LatLon_kwds)
|
|
967
|
+
else:
|
|
968
|
+
c = self.toCartesian()
|
|
969
|
+
c = c.toTransform(transform, inverse=inverse, datum=d)
|
|
970
|
+
r = c.toLatLon(LatLon=self.classof, **_xkwds(LatLon_kwds, height=self.height))
|
|
971
|
+
return r
|
|
972
|
+
|
|
973
|
+
def toUps(self, pole=NN, falsed=True, center=False):
|
|
974
|
+
'''Convert this C{LatLon} point to a UPS coordinate.
|
|
975
|
+
|
|
976
|
+
@kwarg pole: Optional top/center of (stereographic)
|
|
977
|
+
projection (C{str}, 'N[orth]' or 'S[outh]').
|
|
978
|
+
@kwarg falsed: False easting and northing (C{bool}).
|
|
979
|
+
@kwarg center: If C{True}, I{un}-center the UPS
|
|
980
|
+
to its C{lowerleft} (C{bool}) or
|
|
981
|
+
by C{B{center} meter} (C{scalar}).
|
|
982
|
+
|
|
983
|
+
@return: The UPS coordinate (L{Ups}).
|
|
984
|
+
|
|
985
|
+
@see: Function L{pygeodesy.toUps8}.
|
|
986
|
+
'''
|
|
987
|
+
if self._upsOK(pole, falsed):
|
|
988
|
+
u = self._ups
|
|
989
|
+
else:
|
|
990
|
+
ups = _MODS.ups
|
|
991
|
+
u = ups.toUps8(self, datum=self.datum, Ups=ups.Ups,
|
|
992
|
+
pole=pole, falsed=falsed)
|
|
993
|
+
return _lowerleft(u, center)
|
|
994
|
+
|
|
995
|
+
def toUtm(self, center=False):
|
|
996
|
+
'''Convert this C{LatLon} point to a UTM coordinate.
|
|
997
|
+
|
|
998
|
+
@kwarg center: If C{True}, I{un}-center the UTM
|
|
999
|
+
to its C{lowerleft} (C{bool}) or
|
|
1000
|
+
by C{B{center} meter} (C{scalar}).
|
|
1001
|
+
|
|
1002
|
+
@return: The UTM coordinate (L{Utm}).
|
|
1003
|
+
|
|
1004
|
+
@see: Method L{Mgrs.toUtm} and function L{pygeodesy.toUtm8}.
|
|
1005
|
+
'''
|
|
1006
|
+
return _lowerleft(self._utm, center)
|
|
1007
|
+
|
|
1008
|
+
def toUtmUps(self, pole=NN, center=False):
|
|
1009
|
+
'''Convert this C{LatLon} point to a UTM or UPS coordinate.
|
|
1010
|
+
|
|
1011
|
+
@kwarg pole: Optional top/center of UPS (stereographic)
|
|
1012
|
+
projection (C{str}, 'N[orth]' or 'S[outh]').
|
|
1013
|
+
@kwarg center: If C{True}, I{un}-center the UTM or UPS to
|
|
1014
|
+
its C{lowerleft} (C{bool}) or by C{B{center}
|
|
1015
|
+
meter} (C{scalar}).
|
|
1016
|
+
|
|
1017
|
+
@return: The UTM or UPS coordinate (L{Utm} or L{Ups}).
|
|
1018
|
+
|
|
1019
|
+
@see: Function L{pygeodesy.toUtmUps8}.
|
|
1020
|
+
'''
|
|
1021
|
+
if self._utmOK():
|
|
1022
|
+
u = self._utm
|
|
1023
|
+
elif self._upsOK(pole):
|
|
1024
|
+
u = self._ups
|
|
1025
|
+
else: # no cover
|
|
1026
|
+
utmups = _MODS.utmups
|
|
1027
|
+
u = utmups.toUtmUps8(self, datum=self.datum, pole=pole, name=self.name,
|
|
1028
|
+
Utm=utmups.Utm, Ups=utmups.Ups)
|
|
1029
|
+
if isinstance(u, utmups.Utm):
|
|
1030
|
+
self._update(False, _utm=u) # PYCHOK kwds
|
|
1031
|
+
elif isinstance(u, utmups.Ups):
|
|
1032
|
+
self._update(False, _ups=u) # PYCHOK kwds
|
|
1033
|
+
else:
|
|
1034
|
+
_xinstanceof(utmups.Utm, utmups.Ups, toUtmUps8=u)
|
|
1035
|
+
return _lowerleft(u, center)
|
|
1036
|
+
|
|
1037
|
+
@deprecated_method
|
|
1038
|
+
def to3xyz(self): # PYCHOK no cover
|
|
1039
|
+
'''DEPRECATED, use method C{toEcef}.
|
|
1040
|
+
|
|
1041
|
+
@return: A L{Vector3Tuple}C{(x, y, z)}.
|
|
1042
|
+
|
|
1043
|
+
@note: Overloads C{LatLonBase.to3xyz}
|
|
1044
|
+
'''
|
|
1045
|
+
r = self.toEcef()
|
|
1046
|
+
return _MODS.namedTuples.Vector3Tuple(r.x, r.y, r.z, name=self.name)
|
|
1047
|
+
|
|
1048
|
+
def triangulate(self, bearing1, other, bearing2, **height_wrap_tol):
|
|
1049
|
+
'''I{Iteratively} locate a point given this, an other point and the (initial)
|
|
1050
|
+
bearing at this and at the other point.
|
|
1051
|
+
|
|
1052
|
+
@arg bearing1: Bearing at this point (compass C{degrees360}).
|
|
1053
|
+
@arg other: Start point of the other line (C{LatLon}).
|
|
1054
|
+
@arg bearing2: Bearing at the other point (compass C{degrees360}).
|
|
1055
|
+
@kwarg height_wrap_tol: Optional keyword arguments C{B{height}=None},
|
|
1056
|
+
C{B{wrap}=False} and C{B{tol}}, see method L{intersection3}.
|
|
1057
|
+
|
|
1058
|
+
@return: Triangulated point (C{LatLon}).
|
|
1059
|
+
|
|
1060
|
+
@see: Method L{intersection3} for further details.
|
|
1061
|
+
'''
|
|
1062
|
+
if _isDegrees(bearing1) and _isDegrees(bearing2):
|
|
1063
|
+
r = self.intersection3(bearing1, other, bearing2, **height_wrap_tol)
|
|
1064
|
+
return r.point
|
|
1065
|
+
raise _TypeError(bearing1=bearing1, bearing2=bearing2 **height_wrap_tol)
|
|
1066
|
+
|
|
1067
|
+
def trilaterate5(self, distance1, point2, distance2, point3, distance3,
|
|
1068
|
+
area=True, eps=EPS1, wrap=False):
|
|
1069
|
+
'''Trilaterate three points by I{area overlap} or I{perimeter
|
|
1070
|
+
intersection} of three intersecting circles.
|
|
1071
|
+
|
|
1072
|
+
@arg distance1: Distance to this point (C{meter}), same units
|
|
1073
|
+
as B{C{eps}}).
|
|
1074
|
+
@arg point2: Second center point (C{LatLon}).
|
|
1075
|
+
@arg distance2: Distance to point2 (C{meter}, same units as
|
|
1076
|
+
B{C{eps}}).
|
|
1077
|
+
@arg point3: Third center point (C{LatLon}).
|
|
1078
|
+
@arg distance3: Distance to point3 (C{meter}, same units as
|
|
1079
|
+
B{C{eps}}).
|
|
1080
|
+
@kwarg area: If C{True} compute the area overlap, otherwise the
|
|
1081
|
+
perimeter intersection of the circles (C{bool}).
|
|
1082
|
+
@kwarg eps: The required I{minimal overlap} for C{B{area}=True}
|
|
1083
|
+
or the I{intersection margin} for C{B{area}=False}
|
|
1084
|
+
(C{meter}, conventionally).
|
|
1085
|
+
@kwarg wrap: If C{True}, wrap or I{normalize} and unroll
|
|
1086
|
+
B{C{point2}} and B{C{point3}} (C{bool}).
|
|
1087
|
+
|
|
1088
|
+
@return: A L{Trilaterate5Tuple}C{(min, minPoint, max, maxPoint, n)}
|
|
1089
|
+
with C{min} and C{max} in C{meter}, same units as B{C{eps}},
|
|
1090
|
+
the corresponding trilaterated points C{minPoint} and
|
|
1091
|
+
C{maxPoint} as I{ellipsoidal} C{LatLon} and C{n}, the number
|
|
1092
|
+
of trilatered points found for the given B{C{eps}}.
|
|
1093
|
+
|
|
1094
|
+
If only a single trilaterated point is found, C{min I{is}
|
|
1095
|
+
max}, C{minPoint I{is} maxPoint} and C{n = 1}.
|
|
1096
|
+
|
|
1097
|
+
For C{B{area}=True}, C{min} and C{max} are the smallest
|
|
1098
|
+
respectively largest I{radial} overlap found.
|
|
1099
|
+
|
|
1100
|
+
For C{B{area}=False}, C{min} and C{max} represent the
|
|
1101
|
+
nearest respectively farthest intersection margin.
|
|
1102
|
+
|
|
1103
|
+
If C{B{area}=True} and all 3 circles are concentric, C{n=0}
|
|
1104
|
+
and C{minPoint} and C{maxPoint} are the B{C{point#}} with
|
|
1105
|
+
the smallest B{C{distance#}} C{min} respectively C{max} the
|
|
1106
|
+
largest B{C{distance#}}.
|
|
1107
|
+
|
|
1108
|
+
@raise IntersectionError: Trilateration failed for the given B{C{eps}},
|
|
1109
|
+
insufficient overlap for C{B{area}=True}, no
|
|
1110
|
+
circle intersections for C{B{area}=False} or
|
|
1111
|
+
all circles are (near-)concentric.
|
|
1112
|
+
|
|
1113
|
+
@raise TypeError: Invalid B{C{point2}} or B{C{point3}}.
|
|
1114
|
+
|
|
1115
|
+
@raise ValueError: Coincident B{C{points}} or invalid B{C{distance1}},
|
|
1116
|
+
B{C{distance2}} or B{C{distance3}}.
|
|
1117
|
+
|
|
1118
|
+
@note: Ellipsoidal trilateration invokes methods C{LatLon.intersections2}
|
|
1119
|
+
and C{LatLon.nearestOn} based on I{Karney}'s Python U{geographiclib
|
|
1120
|
+
<https://PyPI.org/project/geographiclib>} if installed, otherwise
|
|
1121
|
+
the accurate (but slower) C{ellipsoidalExact.LatLon} methods.
|
|
1122
|
+
'''
|
|
1123
|
+
return _trilaterate5(self, distance1,
|
|
1124
|
+
self.others(point2=point2), distance2,
|
|
1125
|
+
self.others(point3=point3), distance3,
|
|
1126
|
+
area=area, eps=eps, wrap=wrap)
|
|
1127
|
+
|
|
1128
|
+
@Property_RO
|
|
1129
|
+
def _ups(self): # __dict__ value overwritten by method C{toUtmUps}
|
|
1130
|
+
'''(INTERNAL) Get this C{LatLon} point as UPS coordinate (L{Ups}),
|
|
1131
|
+
see L{pygeodesy.toUps8}.
|
|
1132
|
+
'''
|
|
1133
|
+
ups = _MODS.ups
|
|
1134
|
+
return ups.toUps8(self, datum=self.datum, Ups=ups.Ups,
|
|
1135
|
+
pole=NN, falsed=True, name=self.name)
|
|
1136
|
+
|
|
1137
|
+
def _upsOK(self, pole=NN, falsed=True):
|
|
1138
|
+
'''(INTERNAL) Check matching C{Ups}.
|
|
1139
|
+
'''
|
|
1140
|
+
try:
|
|
1141
|
+
u = self._ups
|
|
1142
|
+
except RangeError:
|
|
1143
|
+
return False
|
|
1144
|
+
return falsed and (u.pole == pole[:1].upper() or not pole)
|
|
1145
|
+
|
|
1146
|
+
@Property_RO
|
|
1147
|
+
def _utm(self): # __dict__ value overwritten by method C{toUtmUps}
|
|
1148
|
+
'''(INTERNAL) Get this C{LatLon} point as UTM coordinate (L{Utm}),
|
|
1149
|
+
see L{pygeodesy.toUtm8}.
|
|
1150
|
+
'''
|
|
1151
|
+
utm = _MODS.utm
|
|
1152
|
+
return utm.toUtm8(self, datum=self.datum, Utm=utm.Utm, name=self.name)
|
|
1153
|
+
|
|
1154
|
+
def _utmOK(self):
|
|
1155
|
+
'''(INTERNAL) Check C{Utm}.
|
|
1156
|
+
'''
|
|
1157
|
+
try:
|
|
1158
|
+
_ = self._utm
|
|
1159
|
+
except RangeError:
|
|
1160
|
+
return False
|
|
1161
|
+
return True
|
|
1162
|
+
|
|
1163
|
+
|
|
1164
|
+
def _lowerleft(utmups, center):
|
|
1165
|
+
'''(INTERNAL) Optionally I{un}-center C{utmups}.
|
|
1166
|
+
'''
|
|
1167
|
+
if center in (False, 0, _0_0):
|
|
1168
|
+
u = utmups
|
|
1169
|
+
elif center in (True,):
|
|
1170
|
+
u = utmups._lowerleft
|
|
1171
|
+
else:
|
|
1172
|
+
u = _MODS.utmupsBase._lowerleft(utmups, center)
|
|
1173
|
+
return u
|
|
1174
|
+
|
|
1175
|
+
|
|
1176
|
+
def _nearestOn(point, point1, point2, within=True, height=None, wrap=False, # was=True
|
|
1177
|
+
equidistant=None, tol=_TOL_M, **LatLon_and_kwds):
|
|
1178
|
+
'''(INTERNAL) Get closest point, imported by .ellipsoidalExact,
|
|
1179
|
+
-GeodSolve, -Karney and -Vincenty to embellish exceptions.
|
|
1180
|
+
'''
|
|
1181
|
+
try:
|
|
1182
|
+
p = _xellipsoidal(point=point)
|
|
1183
|
+
t = _MODS.ellipsoidalBaseDI._nearestOn2(p, point1, point2, within=within,
|
|
1184
|
+
height=height, wrap=wrap,
|
|
1185
|
+
equidistant=equidistant,
|
|
1186
|
+
tol=tol, **LatLon_and_kwds)
|
|
1187
|
+
except (TypeError, ValueError) as x:
|
|
1188
|
+
raise _xError(x, point=point, point1=point1, point2=point2)
|
|
1189
|
+
return t.closest
|
|
1190
|
+
|
|
1191
|
+
|
|
1192
|
+
def _set_reframe(inst, reframe):
|
|
1193
|
+
'''(INTERNAL) Set or clear an instance's reference frame.
|
|
1194
|
+
'''
|
|
1195
|
+
if reframe is not None:
|
|
1196
|
+
_xinstanceof(_MODS.trf.RefFrame, reframe=reframe)
|
|
1197
|
+
inst._reframe = reframe
|
|
1198
|
+
elif inst.reframe is not None:
|
|
1199
|
+
inst._reframe = None
|
|
1200
|
+
|
|
1201
|
+
|
|
1202
|
+
__all__ += _ALL_DOCS(CartesianEllipsoidalBase, LatLonEllipsoidalBase)
|
|
1203
|
+
|
|
1204
|
+
# **) MIT License
|
|
1205
|
+
#
|
|
1206
|
+
# Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
|
|
1207
|
+
#
|
|
1208
|
+
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
1209
|
+
# copy of this software and associated documentation files (the "Software"),
|
|
1210
|
+
# to deal in the Software without restriction, including without limitation
|
|
1211
|
+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
1212
|
+
# and/or sell copies of the Software, and to permit persons to whom the
|
|
1213
|
+
# Software is furnished to do so, subject to the following conditions:
|
|
1214
|
+
#
|
|
1215
|
+
# The above copyright notice and this permission notice shall be included
|
|
1216
|
+
# in all copies or substantial portions of the Software.
|
|
1217
|
+
#
|
|
1218
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
1219
|
+
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
1220
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
1221
|
+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
1222
|
+
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
1223
|
+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
1224
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|