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.
Files changed (115) hide show
  1. PyGeodesy-24.3.24.dist-info/METADATA +272 -0
  2. PyGeodesy-24.3.24.dist-info/RECORD +115 -0
  3. PyGeodesy-24.3.24.dist-info/WHEEL +6 -0
  4. PyGeodesy-24.3.24.dist-info/top_level.txt +1 -0
  5. pygeodesy/LICENSE +21 -0
  6. pygeodesy/__init__.py +615 -0
  7. pygeodesy/__main__.py +103 -0
  8. pygeodesy/albers.py +867 -0
  9. pygeodesy/auxilats/_CX_4.py +218 -0
  10. pygeodesy/auxilats/_CX_6.py +314 -0
  11. pygeodesy/auxilats/_CX_8.py +475 -0
  12. pygeodesy/auxilats/__init__.py +54 -0
  13. pygeodesy/auxilats/__main__.py +86 -0
  14. pygeodesy/auxilats/auxAngle.py +548 -0
  15. pygeodesy/auxilats/auxDLat.py +302 -0
  16. pygeodesy/auxilats/auxDST.py +296 -0
  17. pygeodesy/auxilats/auxLat.py +848 -0
  18. pygeodesy/auxilats/auxily.py +272 -0
  19. pygeodesy/azimuthal.py +1150 -0
  20. pygeodesy/basics.py +892 -0
  21. pygeodesy/booleans.py +2031 -0
  22. pygeodesy/cartesianBase.py +1062 -0
  23. pygeodesy/clipy.py +704 -0
  24. pygeodesy/constants.py +516 -0
  25. pygeodesy/css.py +660 -0
  26. pygeodesy/datums.py +752 -0
  27. pygeodesy/deprecated/__init__.py +61 -0
  28. pygeodesy/deprecated/bases.py +40 -0
  29. pygeodesy/deprecated/classes.py +262 -0
  30. pygeodesy/deprecated/consterns.py +54 -0
  31. pygeodesy/deprecated/datum.py +40 -0
  32. pygeodesy/deprecated/functions.py +375 -0
  33. pygeodesy/deprecated/nvector.py +48 -0
  34. pygeodesy/deprecated/rhumbBase.py +32 -0
  35. pygeodesy/deprecated/rhumbaux.py +33 -0
  36. pygeodesy/deprecated/rhumbsolve.py +33 -0
  37. pygeodesy/deprecated/rhumbx.py +33 -0
  38. pygeodesy/dms.py +986 -0
  39. pygeodesy/ecef.py +1348 -0
  40. pygeodesy/elevations.py +279 -0
  41. pygeodesy/ellipsoidalBase.py +1224 -0
  42. pygeodesy/ellipsoidalBaseDI.py +913 -0
  43. pygeodesy/ellipsoidalExact.py +343 -0
  44. pygeodesy/ellipsoidalGeodSolve.py +343 -0
  45. pygeodesy/ellipsoidalKarney.py +403 -0
  46. pygeodesy/ellipsoidalNvector.py +685 -0
  47. pygeodesy/ellipsoidalVincenty.py +590 -0
  48. pygeodesy/ellipsoids.py +2476 -0
  49. pygeodesy/elliptic.py +1198 -0
  50. pygeodesy/epsg.py +243 -0
  51. pygeodesy/errors.py +804 -0
  52. pygeodesy/etm.py +1190 -0
  53. pygeodesy/fmath.py +1013 -0
  54. pygeodesy/formy.py +1818 -0
  55. pygeodesy/frechet.py +865 -0
  56. pygeodesy/fstats.py +760 -0
  57. pygeodesy/fsums.py +1898 -0
  58. pygeodesy/gars.py +358 -0
  59. pygeodesy/geodesicw.py +581 -0
  60. pygeodesy/geodesicx/_C4_24.py +1699 -0
  61. pygeodesy/geodesicx/_C4_27.py +2395 -0
  62. pygeodesy/geodesicx/_C4_30.py +3301 -0
  63. pygeodesy/geodesicx/__init__.py +48 -0
  64. pygeodesy/geodesicx/__main__.py +91 -0
  65. pygeodesy/geodesicx/gx.py +1382 -0
  66. pygeodesy/geodesicx/gxarea.py +535 -0
  67. pygeodesy/geodesicx/gxbases.py +154 -0
  68. pygeodesy/geodesicx/gxline.py +669 -0
  69. pygeodesy/geodsolve.py +426 -0
  70. pygeodesy/geohash.py +914 -0
  71. pygeodesy/geoids.py +1884 -0
  72. pygeodesy/hausdorff.py +892 -0
  73. pygeodesy/heights.py +1155 -0
  74. pygeodesy/interns.py +687 -0
  75. pygeodesy/iters.py +545 -0
  76. pygeodesy/karney.py +919 -0
  77. pygeodesy/ktm.py +633 -0
  78. pygeodesy/latlonBase.py +1766 -0
  79. pygeodesy/lazily.py +960 -0
  80. pygeodesy/lcc.py +684 -0
  81. pygeodesy/ltp.py +1107 -0
  82. pygeodesy/ltpTuples.py +1563 -0
  83. pygeodesy/mgrs.py +721 -0
  84. pygeodesy/named.py +1324 -0
  85. pygeodesy/namedTuples.py +683 -0
  86. pygeodesy/nvectorBase.py +695 -0
  87. pygeodesy/osgr.py +781 -0
  88. pygeodesy/points.py +1686 -0
  89. pygeodesy/props.py +628 -0
  90. pygeodesy/resections.py +1048 -0
  91. pygeodesy/rhumb/__init__.py +46 -0
  92. pygeodesy/rhumb/aux_.py +397 -0
  93. pygeodesy/rhumb/bases.py +1148 -0
  94. pygeodesy/rhumb/ekx.py +563 -0
  95. pygeodesy/rhumb/solve.py +572 -0
  96. pygeodesy/simplify.py +647 -0
  97. pygeodesy/solveBase.py +472 -0
  98. pygeodesy/sphericalBase.py +724 -0
  99. pygeodesy/sphericalNvector.py +1264 -0
  100. pygeodesy/sphericalTrigonometry.py +1447 -0
  101. pygeodesy/streprs.py +627 -0
  102. pygeodesy/trf.py +2079 -0
  103. pygeodesy/triaxials.py +1484 -0
  104. pygeodesy/units.py +969 -0
  105. pygeodesy/unitsBase.py +349 -0
  106. pygeodesy/ups.py +538 -0
  107. pygeodesy/utily.py +1231 -0
  108. pygeodesy/utm.py +762 -0
  109. pygeodesy/utmups.py +318 -0
  110. pygeodesy/utmupsBase.py +517 -0
  111. pygeodesy/vector2d.py +785 -0
  112. pygeodesy/vector3d.py +968 -0
  113. pygeodesy/vector3dBase.py +1049 -0
  114. pygeodesy/webmercator.py +383 -0
  115. pygeodesy/wgrs.py +439 -0
@@ -0,0 +1,695 @@
1
+
2
+ # -*- coding: utf-8 -*-
3
+
4
+ u'''(INTERNAL) Private elliposiodal and spherical C{Nvector} base classes
5
+ L{LatLonNvectorBase} and L{NvectorBase} and function L{sumOf}.
6
+
7
+ Pure Python implementation of C{n-vector}-based geodesy tools for ellipsoidal
8
+ earth models, transcoded from JavaScript originals by I{(C) Chris Veness 2005-2016}
9
+ and published under the same MIT Licence**, see U{Vector-based geodesy
10
+ <https://www.Movable-Type.co.UK/scripts/latlong-vectors.html>}.
11
+ '''
12
+
13
+ # from pygeodesy.basics import map1 # from .namedTuples
14
+ from pygeodesy.constants import EPS, EPS1, EPS_2, R_M, _2_0, _N_2_0
15
+ # from pygeodesy.datums import _spherical_datum # from .formy
16
+ from pygeodesy.errors import IntersectionError, _ValueError, VectorError, \
17
+ _xkwds, _xkwds_pop2
18
+ from pygeodesy.fmath import fdot, fidw, hypot_ # PYCHOK fdot shared
19
+ from pygeodesy.fsums import Fsum, fsumf_
20
+ from pygeodesy.formy import _isequalTo, n_xyz2latlon, n_xyz2philam, \
21
+ _spherical_datum
22
+ from pygeodesy.interns import NN, _1_, _2_, _3_, _bearing_, _coincident_, \
23
+ _COMMASPACE_, _distance_, _h_, _insufficient_, \
24
+ _intersection_, _no_, _NorthPole_, _point_, \
25
+ _pole_, _SPACE_, _SouthPole_, _under
26
+ from pygeodesy.latlonBase import LatLonBase, _ALL_DOCS, _ALL_LAZY, _MODS
27
+ # from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS # from .latlonBase
28
+ from pygeodesy.named import notImplemented, _xother3
29
+ from pygeodesy.namedTuples import Trilaterate5Tuple, Vector3Tuple, \
30
+ Vector4Tuple, map1
31
+ from pygeodesy.props import deprecated_method, Property_RO, property_doc_, \
32
+ property_RO, _update_all
33
+ from pygeodesy.streprs import Fmt, hstr, unstr, _xattrs
34
+ from pygeodesy.units import Bearing, Height, Radius_, Scalar
35
+ from pygeodesy.utily import sincos2d, _unrollon, _unrollon3
36
+ from pygeodesy.vector3d import Vector3d, _xyzhdn3
37
+
38
+ from math import fabs, sqrt
39
+
40
+ __all__ = _ALL_LAZY.nvectorBase
41
+ __version__ = '24.02.18'
42
+
43
+
44
+ class NvectorBase(Vector3d): # XXX kept private
45
+ '''Base class for ellipsoidal and spherical C{Nvector}s.
46
+ '''
47
+ _datum = None # L{Datum}, overriden
48
+ _h = Height(h=0) # height (C{meter})
49
+ _H = NN # height prefix (C{str}), '↑' in JS version
50
+
51
+ def __init__(self, x_xyz, y=None, z=None, h=0, ll=None, datum=None, name=NN):
52
+ '''New n-vector normal to the earth's surface.
53
+
54
+ @arg x_xyz: X component of vector (C{scalar}) or (3-D) vector
55
+ (C{Nvector}, L{Vector3d}, L{Vector3Tuple} or
56
+ L{Vector4Tuple}).
57
+ @kwarg y: Y component of vector (C{scalar}), ignored if B{C{x_xyz}}
58
+ is not C{scalar}, otherwise same units as B{C{x_xyz}}.
59
+ @kwarg z: Z component of vector (C{scalar}), ignored if B{C{x_xyz}}
60
+ is not C{scalar}, otherwise same units as B{C{x_xyz}}.
61
+ @kwarg h: Optional height above surface (C{meter}).
62
+ @kwarg ll: Optional, original latlon (C{LatLon}).
63
+ @kwarg datum: Optional, I{pass-thru} datum (L{Datum}).
64
+ @kwarg name: Optional name (C{str}).
65
+
66
+ @raise TypeError: Non-scalar B{C{x}}, B{C{y}} or B{C{z}}
67
+ coordinate or B{C{x}} not an C{Nvector},
68
+ L{Vector3Tuple} or L{Vector4Tuple} or
69
+ invalid B{C{datum}}.
70
+ '''
71
+ h, d, n = _xyzhdn3(x_xyz, h, datum, ll)
72
+ Vector3d.__init__(self, x_xyz, y=y, z=z, ll=ll, name=name or n)
73
+ if h:
74
+ self.h = h
75
+ if d is not None:
76
+ self._datum = _spherical_datum(d, name=self.name) # pass-thru
77
+
78
+ @Property_RO
79
+ def datum(self):
80
+ '''Get the I{pass-thru} datum (C{Datum}) or C{None}.
81
+ '''
82
+ return self._datum
83
+
84
+ @property_RO
85
+ def Ecef(self):
86
+ '''Get the ECEF I{class} (L{EcefKarney}), I{once}.
87
+ '''
88
+ NvectorBase.Ecef = E = _MODS.ecef.EcefKarney # overwrite property_RO
89
+ return E
90
+
91
+ @property_RO
92
+ def ellipsoidalNvector(self):
93
+ '''Get the C{Nvector type} iff ellipsoidal, overloaded in L{pygeodesy.ellipsoidalNvector.Nvector}.
94
+ '''
95
+ return False
96
+
97
+ @property_doc_(''' the height above surface (C{meter}).''')
98
+ def h(self):
99
+ '''Get the height above surface (C{meter}).
100
+ '''
101
+ return self._h
102
+
103
+ @h.setter # PYCHOK setter!
104
+ def h(self, h):
105
+ '''Set the height above surface (C{meter}).
106
+
107
+ @raise TypeError: If B{C{h}} invalid.
108
+
109
+ @raise VectorError: If B{C{h}} invalid.
110
+ '''
111
+ h = Height(h=h, Error=VectorError)
112
+ if self._h != h:
113
+ _update_all(self)
114
+ self._h = h
115
+
116
+ @property_doc_(''' the height prefix (C{str}).''')
117
+ def H(self):
118
+ '''Get the height prefix (C{str}).
119
+ '''
120
+ return self._H
121
+
122
+ @H.setter # PYCHOK setter!
123
+ def H(self, H):
124
+ '''Set the height prefix (C{str}).
125
+ '''
126
+ self._H = str(H) if H else NN
127
+
128
+ def hStr(self, prec=-2, m=NN):
129
+ '''Return a string for the height B{C{h}}.
130
+
131
+ @kwarg prec: Number of (decimal) digits, unstripped (C{int}).
132
+ @kwarg m: Optional unit of the height (C{str}).
133
+
134
+ @see: Function L{pygeodesy.hstr}.
135
+ '''
136
+ return NN(self.H, hstr(self.h, prec=prec, m=m))
137
+
138
+ @Property_RO
139
+ def isEllipsoidal(self):
140
+ '''Check whether this n-vector is ellipsoidal (C{bool} or C{None} if unknown).
141
+ '''
142
+ return self.datum.isEllipsoidal if self.datum else None
143
+
144
+ @Property_RO
145
+ def isSpherical(self):
146
+ '''Check whether this n-vector is spherical (C{bool} or C{None} if unknown).
147
+ '''
148
+ return self.datum.isSpherical if self.datum else None
149
+
150
+ @Property_RO
151
+ def lam(self):
152
+ '''Get the (geodetic) longitude in C{radians} (C{float}).
153
+ '''
154
+ return self.philam.lam
155
+
156
+ @Property_RO
157
+ def lat(self):
158
+ '''Get the (geodetic) latitude in C{degrees} (C{float}).
159
+ '''
160
+ return self.latlon.lat
161
+
162
+ @Property_RO
163
+ def latlon(self):
164
+ '''Get the (geodetic) lat-, longitude in C{degrees} (L{LatLon2Tuple}C{(lat, lon)}).
165
+ '''
166
+ return n_xyz2latlon(self.x, self.y, self.z, name=self.name)
167
+
168
+ @Property_RO
169
+ def latlonheight(self):
170
+ '''Get the (geodetic) lat-, longitude in C{degrees} and height (L{LatLon3Tuple}C{(lat, lon, height)}).
171
+ '''
172
+ return self.latlon.to3Tuple(self.h)
173
+
174
+ @Property_RO
175
+ def latlonheightdatum(self):
176
+ '''Get the lat-, longitude in C{degrees} with height and datum (L{LatLon4Tuple}C{(lat, lon, height, datum)}).
177
+ '''
178
+ return self.latlonheight.to4Tuple(self.datum)
179
+
180
+ @Property_RO
181
+ def lon(self):
182
+ '''Get the (geodetic) longitude in C{degrees} (C{float}).
183
+ '''
184
+ return self.latlon.lon
185
+
186
+ @Property_RO
187
+ def phi(self):
188
+ '''Get the (geodetic) latitude in C{radians} (C{float}).
189
+ '''
190
+ return self.philam.phi
191
+
192
+ @Property_RO
193
+ def philam(self):
194
+ '''Get the (geodetic) lat-, longitude in C{radians} (L{PhiLam2Tuple}C{(phi, lam)}).
195
+ '''
196
+ return n_xyz2philam(self.x, self.y, self.z, name=self.name)
197
+
198
+ @Property_RO
199
+ def philamheight(self):
200
+ '''Get the (geodetic) lat-, longitude in C{radians} and height (L{PhiLam3Tuple}C{(phi, lam, height)}).
201
+ '''
202
+ return self.philam.to3Tuple(self.h)
203
+
204
+ @Property_RO
205
+ def philamheightdatum(self):
206
+ '''Get the lat-, longitude in C{radians} with height and datum (L{PhiLam4Tuple}C{(phi, lam, height, datum)}).
207
+ '''
208
+ return self.philamheight.to4Tuple(self.datum)
209
+
210
+ @property_RO
211
+ def sphericalNvector(self):
212
+ '''Get the C{Nvector type} iff spherical, overloaded in L{pygeodesy.sphericalNvector.Nvector}.
213
+ '''
214
+ return False
215
+
216
+ @deprecated_method
217
+ def to2ab(self): # PYCHOK no cover
218
+ '''DEPRECATED, use property L{philam}.
219
+
220
+ @return: A L{PhiLam2Tuple}C{(phi, lam)}.
221
+ '''
222
+ return self.philam
223
+
224
+ @deprecated_method
225
+ def to3abh(self, height=None): # PYCHOK no cover
226
+ '''DEPRECATED, use property L{philamheight} or C{philam.to3Tuple(B{height})}.
227
+
228
+ @kwarg height: Optional height, overriding this
229
+ n-vector's height (C{meter}).
230
+
231
+ @return: A L{PhiLam3Tuple}C{(phi, lam, height)}.
232
+
233
+ @raise ValueError: Invalid B{C{height}}.
234
+ '''
235
+ return self.philamheight if height in (None, self.h) else \
236
+ self.philam.to3Tuple(height)
237
+
238
+ def toCartesian(self, h=None, Cartesian=None, datum=None, **Cartesian_kwds):
239
+ '''Convert this n-vector to C{Nvector}-based cartesian (ECEF) coordinates.
240
+
241
+ @kwarg h: Optional height, overriding this n-vector's height (C{meter}).
242
+ @kwarg Cartesian: Optional class to return the (ECEF) coordinates
243
+ (C{Cartesian}).
244
+ @kwarg datum: Optional datum (C{Datum}), overriding this datum.
245
+ @kwarg Cartesian_kwds: Optional, additional B{C{Cartesian}} keyword
246
+ arguments, ignored if C{B{Cartesian} is None}.
247
+
248
+ @return: The cartesian (ECEF) coordinates (B{C{Cartesian}}) or
249
+ if C{B{Cartesian} is None}, an L{Ecef9Tuple}C{(x, y, z,
250
+ lat, lon, height, C, M, datum)} with C{C} and C{M} if
251
+ available.
252
+
253
+ @raise TypeError: Invalid B{C{Cartesian}} or B{C{Cartesian_kwds}}
254
+ argument.
255
+
256
+ @raise ValueError: Invalid B{C{h}}.
257
+ '''
258
+ D = _spherical_datum(datum or self.datum, name=self.name)
259
+ E = D.ellipsoid
260
+ h = self.h if h is None else Height(h)
261
+
262
+ x, y, z = self.x, self.y, self.z
263
+ # Kenneth Gade eqn 22
264
+ n = E.b / hypot_(x * E.a_b, y * E.a_b, z)
265
+ r = h + n * E.a2_b2
266
+
267
+ x *= r
268
+ y *= r
269
+ z *= h + n
270
+
271
+ if Cartesian is None:
272
+ r = self.Ecef(D).reverse(x, y, z, M=True)
273
+ else:
274
+ kwds = _xkwds(Cartesian_kwds, datum=D) # h=0
275
+ r = Cartesian(x, y, z, **kwds)
276
+ return self._xnamed(r)
277
+
278
+ @deprecated_method
279
+ def to2ll(self): # PYCHOK no cover
280
+ '''DEPRECATED, use property L{latlon}.
281
+
282
+ @return: A L{LatLon2Tuple}C{(lat, lon)}.
283
+ '''
284
+ return self.latlon
285
+
286
+ @deprecated_method
287
+ def to3llh(self, height=None): # PYCHOK no cover
288
+ '''DEPRECATED, use property C{latlonheight} or C{latlon.to3Tuple(B{height})}.
289
+
290
+ @kwarg height: Optional height, overriding this
291
+ n-vector's height (C{meter}).
292
+
293
+ @return: A L{LatLon3Tuple}C{(lat, lon, height)}.
294
+
295
+ @raise ValueError: Invalid B{C{height}}.
296
+ '''
297
+ return self.latlonheight if height in (None, self.h) else \
298
+ self.latlon.to3Tuple(height)
299
+
300
+ def toLatLon(self, height=None, LatLon=None, datum=None, **LatLon_kwds):
301
+ '''Convert this n-vector to an C{Nvector}-based geodetic point.
302
+
303
+ @kwarg height: Optional height, overriding this n-vector's
304
+ height (C{meter}).
305
+ @kwarg LatLon: Optional class to return the geodetic point
306
+ (C{LatLon}) or C{None}.
307
+ @kwarg datum: Optional, spherical datum (C{Datum}).
308
+ @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword
309
+ arguments, ignored if C{B{LatLon} is None}.
310
+
311
+ @return: The geodetic point (C{LatLon}) or if C{B{LatLon} is None},
312
+ an L{Ecef9Tuple}C{(x, y, z, lat, lon, height, C, M,
313
+ datum)} with C{C} and C{M} if available.
314
+
315
+ @raise TypeError: Invalid B{C{LatLon}} or B{C{LatLon_kwds}}
316
+ argument.
317
+
318
+ @raise ValueError: Invalid B{C{height}}.
319
+ '''
320
+ d = _spherical_datum(datum or self.datum, name=self.name)
321
+ h = self.h if height is None else Height(height)
322
+ # use self.Cartesian(Cartesian=None) for better accuracy of the height
323
+ # than self.Ecef(d).forward(self.lat, self.lon, height=h, M=True)
324
+ if LatLon is None:
325
+ r = self.toCartesian(h=h, Cartesian=None, datum=d)
326
+ else:
327
+ kwds = _xkwds(LatLon_kwds, height=h, datum=d)
328
+ r = self._xnamed(LatLon(self.lat, self.lon, **kwds))
329
+ return r
330
+
331
+ def toStr(self, prec=5, fmt=Fmt.PAREN, sep=_COMMASPACE_): # PYCHOK expected
332
+ '''Return a string representation of this n-vector.
333
+
334
+ Height component is only included if non-zero.
335
+
336
+ @kwarg prec: Number of (decimal) digits, unstripped (C{int}).
337
+ @kwarg fmt: Enclosing backets format (C{str}).
338
+ @kwarg sep: Optional separator between components (C{str}).
339
+
340
+ @return: Comma-separated C{"(x, y, z [, h])"} enclosed in
341
+ B{C{fmt}} brackets (C{str}).
342
+ '''
343
+ t = Vector3d.toStr(self, prec=prec, fmt=NN, sep=sep)
344
+ if self.h:
345
+ t = sep.join((t, self.hStr()))
346
+ return (fmt % (t,)) if fmt else t
347
+
348
+ def toVector3d(self, norm=True):
349
+ '''Convert this n-vector to a 3-D vector, I{ignoring height}.
350
+
351
+ @kwarg norm: Normalize the 3-D vector (C{bool}).
352
+
353
+ @return: The (normalized) vector (L{Vector3d}).
354
+ '''
355
+ v = Vector3d.unit(self) if norm else self
356
+ return Vector3d(v.x, v.y, v.z, name=self.name)
357
+
358
+ @deprecated_method
359
+ def to4xyzh(self, h=None): # PYCHOK no cover
360
+ '''DEPRECATED, use property L{xyzh} or C{xyz.to4Tuple(B{h})}.'''
361
+ return self.xyzh if h in (None, self.h) else Vector4Tuple(
362
+ self.x, self.y, self.z, h, name=self.name)
363
+
364
+ def unit(self, ll=None):
365
+ '''Normalize this n-vector to unit length.
366
+
367
+ @kwarg ll: Optional, original latlon (C{LatLon}).
368
+
369
+ @return: Normalized vector (C{Nvector}).
370
+ '''
371
+ return _xattrs(Vector3d.unit(self, ll=ll), _under(_h_))
372
+
373
+ @Property_RO
374
+ def xyzh(self):
375
+ '''Get this n-vector's components (L{Vector4Tuple}C{(x, y, z, h)})
376
+ '''
377
+ return self.xyz.to4Tuple(self.h)
378
+
379
+
380
+ NorthPole = NvectorBase(0, 0, +1, name=_NorthPole_) # North pole (C{Nvector})
381
+ SouthPole = NvectorBase(0, 0, -1, name=_SouthPole_) # South pole (C{Nvector})
382
+
383
+
384
+ class _N_vector_(NvectorBase):
385
+ '''(INTERNAL) Minimal, low-overhead C{n-vector}.
386
+ '''
387
+ def __init__(self, x, y, z, h=0, name=NN):
388
+ self._x, self._y, self._z = x, y, z
389
+ if h:
390
+ self._h = h
391
+ if name:
392
+ self.name = name
393
+
394
+
395
+ class LatLonNvectorBase(LatLonBase):
396
+ '''(INTERNAL) Base class for n-vector-based ellipsoidal and
397
+ spherical C{LatLon} classes.
398
+ '''
399
+
400
+ def _update(self, updated, *attrs, **setters): # PYCHOK _Nv=None
401
+ '''(INTERNAL) Zap cached attributes if updated.
402
+
403
+ @see: C{ellipsoidalNvector.LatLon} and C{sphericalNvector.LatLon}
404
+ for the special case of B{C{_Nv}}.
405
+ '''
406
+ if updated:
407
+ _Nv, setters = _xkwds_pop2(setters, _Nv=None)
408
+ if _Nv is not None:
409
+ if _Nv._fromll is not None:
410
+ _Nv._fromll = None
411
+ self._Nv = None
412
+ LatLonBase._update(self, updated, *attrs, **setters)
413
+
414
+ # def distanceTo(self, other, **kwds): # PYCHOK no cover
415
+ # '''I{Must be overloaded}.'''
416
+ # _MODS.named.notOverloaded(self, other, **kwds)
417
+
418
+ def intersections2(self, radius1, other, radius2, **kwds): # PYCHOK expected
419
+ '''B{Not implemented}, throws a C{NotImplementedError} always.'''
420
+ notImplemented(self, radius1, other, radius2, **kwds)
421
+
422
+ def others(self, *other, **name_other_up):
423
+ '''Refined class comparison.
424
+
425
+ @arg other: The other instance (C{LatLonNvectorBase}).
426
+ @kwarg name_other_up: Overriding C{name=other} and C{up=1}
427
+ keyword arguments.
428
+
429
+ @return: The B{C{other}} if compatible.
430
+
431
+ @raise TypeError: Incompatible B{C{other}} C{type}.
432
+ '''
433
+ if other:
434
+ other0 = other[0]
435
+ if isinstance(other0, (self.__class__, LatLonNvectorBase)): # XXX NvectorBase?
436
+ return other0
437
+
438
+ other, name, up = _xother3(self, other, **name_other_up)
439
+ if not isinstance(other, (self.__class__, LatLonNvectorBase)): # XXX NvectorBase?
440
+ LatLonBase.others(self, other, name=name, up=up + 1)
441
+ return other
442
+
443
+ def toNvector(self, **Nvector_and_kwds): # PYCHOK signature
444
+ '''Convert this point to C{Nvector} components, I{including height}.
445
+
446
+ @kwarg Nvector_and_kwds: Optional C{Nvector} class and C{Nvector} keyword arguments,
447
+ Specify C{B{Nvector}=...} to override this C{Nvector} class
448
+ or use C{B{Nvector}=None}.
449
+
450
+ @return: An C{Nvector} or if C{Nvector is None}, a L{Vector4Tuple}C{(x, y, z, h)}.
451
+
452
+ @raise TypeError: Invalid C{Nvector} or other B{C{Nvector_and_kwds}} item.
453
+ '''
454
+ return LatLonBase.toNvector(self, **_xkwds(Nvector_and_kwds, Nvector=NvectorBase))
455
+
456
+ def triangulate(self, bearing1, other, bearing2, height=None, wrap=False): # PYCHOK signature
457
+ '''Locate a point given this, an other point and the (initial) bearing
458
+ from this and the other point.
459
+
460
+ @arg bearing1: Bearing at this point (compass C{degrees360}).
461
+ @arg other: The other point (C{LatLon}).
462
+ @arg bearing2: Bearing at the other point (compass C{degrees360}).
463
+ @kwarg height: Optional height at the triangulated point,
464
+ overriding the mean height (C{meter}).
465
+ @kwarg wrap: If C{True}, use this and the B{C{other}} point
466
+ I{normalized} (C{bool}).
467
+
468
+ @return: Triangulated point (C{LatLon}).
469
+
470
+ @raise TypeError: Invalid B{C{other}} point.
471
+
472
+ @raise Valuerror: Points coincide.
473
+ '''
474
+ return _triangulate(self, bearing1, self.others(other), bearing2,
475
+ height=height, wrap=wrap, LatLon=self.classof)
476
+
477
+ def trilaterate(self, distance1, point2, distance2, point3, distance3,
478
+ radius=R_M, height=None, useZ=False, wrap=False):
479
+ '''Locate a point at given distances from this and two other points.
480
+
481
+ @arg distance1: Distance to this point (C{meter}, same units
482
+ as B{C{radius}}).
483
+ @arg point2: Second reference point (C{LatLon}).
484
+ @arg distance2: Distance to point2 (C{meter}, same units as
485
+ B{C{radius}}).
486
+ @arg point3: Third reference point (C{LatLon}).
487
+ @arg distance3: Distance to point3 (C{meter}, same units as
488
+ B{C{radius}}).
489
+ @kwarg radius: Mean earth radius (C{meter}).
490
+ @kwarg height: Optional height at trilaterated point, overriding
491
+ the mean height (C{meter}, same units as B{C{radius}}).
492
+ @kwarg useZ: Include Z component iff non-NaN, non-zero (C{bool}).
493
+ @kwarg wrap: If C{True}, use this, B{C{point2}} and B{C{point3}}
494
+ I{normalized} (C{bool}).
495
+
496
+ @return: Trilaterated point (C{LatLon}).
497
+
498
+ @raise IntersectionError: No intersection, trilateration failed.
499
+
500
+ @raise TypeError: Invalid B{C{point2}} or B{C{point3}}.
501
+
502
+ @raise ValueError: Some B{C{points}} coincide or invalid B{C{distance1}},
503
+ B{C{distance2}}, B{C{distance3}} or B{C{radius}}.
504
+
505
+ @see: U{Trilateration<https://WikiPedia.org/wiki/Trilateration>},
506
+ Veness' JavaScript U{Trilateration<https://www.Movable-Type.co.UK/
507
+ scripts/latlong-vectors.html>} and method C{LatLon.trilaterate5}
508
+ of other, non-C{Nvector LatLon} classes.
509
+ '''
510
+ return _trilaterate(self, distance1, self.others(point2=point2), distance2,
511
+ self.others(point3=point3), distance3,
512
+ radius=radius, height=height, useZ=useZ,
513
+ wrap=wrap, LatLon=self.classof)
514
+
515
+ def trilaterate5(self, distance1, point2, distance2, point3, distance3, # PYCHOK signature
516
+ area=False, eps=EPS1, radius=R_M, wrap=False):
517
+ '''B{Not implemented} for C{B{area}=True} and falls back to method
518
+ C{trilaterate} otherwise.
519
+
520
+ @return: A L{Trilaterate5Tuple}C{(min, minPoint, max, maxPoint, n)}
521
+ with a single trilaterated intersection C{minPoint I{is}
522
+ maxPoint}, C{min I{is} max} the nearest intersection
523
+ margin and count C{n = 1}.
524
+
525
+ @raise NotImplementedError: Keyword argument C{B{area}=True} not
526
+ (yet) supported.
527
+
528
+ @see: Method L{trilaterate} for other and more details.
529
+ '''
530
+ if area:
531
+ notImplemented(self, area=area)
532
+
533
+ t = _trilaterate(self, distance1, self.others(point2=point2), distance2,
534
+ self.others(point3=point3), distance3,
535
+ radius=radius, useZ=True, wrap=wrap,
536
+ LatLon=self.classof)
537
+ # ... and handle B{C{eps}} and C{IntersectionError}
538
+ # like function C{.latlonBase._trilaterate5}
539
+ d = self.distanceTo(t, radius=radius, wrap=wrap) # PYCHOK distanceTo
540
+ d = min(fabs(distance1 - d), fabs(distance2 - d), fabs(distance3 - d))
541
+ if d < eps: # min is max, minPoint is maxPoint
542
+ return Trilaterate5Tuple(d, t, d, t, 1) # n = 1
543
+ t = _SPACE_(_no_(_intersection_), Fmt.PAREN(min.__name__, Fmt.f(d, prec=3)))
544
+ raise IntersectionError(area=area, eps=eps, radius=radius, wrap=wrap, txt=t)
545
+
546
+
547
+ def _nsumOf(nvs, h_None, Vector, Vector_kwds): # .sphericalNvector, .vector3d
548
+ '''(INTERNAL) Separated to allow callers to embellish exceptions.
549
+ '''
550
+ X, Y, Z, n = Fsum(), Fsum(), Fsum(), 0
551
+ H = Fsum() if h_None is None else n
552
+ for n, v in enumerate(nvs or ()): # one pass
553
+ X += v.x
554
+ Y += v.y
555
+ Z += v.z
556
+ H += v.h
557
+ if n < 1:
558
+ raise ValueError(_SPACE_(Fmt.PARENSPACED(len=n), _insufficient_))
559
+
560
+ x, y, z = map1(float, X, Y, Z)
561
+ h = H.fover(n) if h_None is None else h_None
562
+ return Vector3Tuple(x, y, z).to4Tuple(h) if Vector is None else \
563
+ Vector(x, y, z, **_xkwds(Vector_kwds, h=h))
564
+
565
+
566
+ def sumOf(nvectors, Vector=None, h=None, **Vector_kwds):
567
+ '''Return the I{vectorial} sum of two or more n-vectors.
568
+
569
+ @arg nvectors: Vectors to be added (C{Nvector}[]).
570
+ @kwarg Vector: Optional class for the vectorial sum (C{Nvector})
571
+ or C{None}.
572
+ @kwarg h: Optional height, overriding the mean height (C{meter}).
573
+ @kwarg Vector_kwds: Optional, additional B{C{Vector}} keyword
574
+ arguments, ignored if C{B{Vector} is None}.
575
+
576
+ @return: Vectorial sum (B{C{Vector}}) or a L{Vector4Tuple}C{(x, y,
577
+ z, h)} if B{C{Vector}} is C{None}.
578
+
579
+ @raise VectorError: No B{C{nvectors}}.
580
+ '''
581
+ try:
582
+ return _nsumOf(nvectors, h, Vector, Vector_kwds)
583
+ except (TypeError, ValueError) as x:
584
+ raise VectorError(nvectors=nvectors, Vector=Vector, cause=x)
585
+
586
+
587
+ def _triangulate(point1, bearing1, point2, bearing2, height=None,
588
+ wrap=False, **LatLon_and_kwds):
589
+ # (INTERNAL) Locate a point given two known points and initial
590
+ # bearings from those points, see C{LatLon.triangulate} above
591
+
592
+ def _gc(p, b, _i_):
593
+ n = p.toNvector()
594
+ de = NorthPole.cross(n, raiser=_pole_).unit() # east vector @ n
595
+ dn = n.cross(de) # north vector @ n
596
+ s, c = sincos2d(Bearing(b, name=_bearing_ + _i_))
597
+ dest = de.times(s)
598
+ dnct = dn.times(c)
599
+ d = dnct.plus(dest) # direction vector @ n
600
+ return n.cross(d) # great circle point + bearing
601
+
602
+ if wrap:
603
+ point2 = _unrollon(point1, point2, wrap=wrap)
604
+ if _isequalTo(point1, point2, eps=EPS):
605
+ raise _ValueError(points=point2, wrap=wrap, txt=_coincident_)
606
+
607
+ gc1 = _gc(point1, bearing1, _1_) # great circle p1 + b1
608
+ gc2 = _gc(point2, bearing2, _2_) # great circle p2 + b2
609
+
610
+ n = gc1.cross(gc2, raiser=_point_) # n-vector of intersection point
611
+ h = point1._havg(point2, h=height)
612
+ kwds = _xkwds(LatLon_and_kwds, height=h)
613
+ return n.toLatLon(**kwds) # Nvector(n.x, n.y, n.z).toLatLon(...)
614
+
615
+
616
+ def _trilaterate(point1, distance1, point2, distance2, point3, distance3,
617
+ radius=R_M, height=None, useZ=False,
618
+ wrap=False, **LatLon_and_kwds):
619
+ # (INTERNAL) Locate a point at given distances from
620
+ # three other points, see LatLon.triangulate above
621
+
622
+ def _nr2(p, d, r, _i_, *qs): # .toNvector and angular distance squared
623
+ for q in qs:
624
+ if _isequalTo(p, q, eps=EPS):
625
+ raise _ValueError(points=p, txt=_coincident_)
626
+ return p.toNvector(), (Scalar(d, name=_distance_ + _i_) / r)**2
627
+
628
+ p1, r = point1, Radius_(radius)
629
+ p2, p3, _ = _unrollon3(p1, point2, point3, wrap)
630
+
631
+ n1, r12 = _nr2(p1, distance1, r, _1_)
632
+ n2, r22 = _nr2(p2, distance2, r, _2_, p1)
633
+ n3, r32 = _nr2(p3, distance3, r, _3_, p1, p2)
634
+
635
+ # the following uses x,y coordinate system with origin at n1, x axis n1->n2
636
+ y = n3.minus(n1)
637
+ x = n2.minus(n1)
638
+ z = None
639
+
640
+ d = x.length # distance n1->n2
641
+ if d > EPS_2: # and y.length > EPS_2:
642
+ X = x.unit() # unit vector in x direction n1->n2
643
+ i = X.dot(y) # signed magnitude of x component of n1->n3
644
+ Y = y.minus(X.times(i)).unit() # unit vector in y direction
645
+ j = Y.dot(y) # signed magnitude of y component of n1->n3
646
+ if fabs(j) > EPS_2:
647
+ # courtesy of U{Carlos Freitas<https://GitHub.com/mrJean1/PyGeodesy/issues/33>}
648
+ x = fsumf_(r12, -r22, d**2) / (d * _2_0) # n1->intersection x- and ...
649
+ y = fsumf_(r12, -r32, i**2, j**2, x * i * _N_2_0) / (j * _2_0) # ... y-component
650
+ # courtesy of U{AleixDev<https://GitHub.com/mrJean1/PyGeodesy/issues/43>}
651
+ z = fsumf_(max(r12, r22, r32), -(x**2), -(y**2)) # XXX not just r12!
652
+ if z > EPS:
653
+ n = n1.plus(X.times(x)).plus(Y.times(y))
654
+ if useZ: # include Z component
655
+ Z = X.cross(Y) # unit vector perpendicular to plane
656
+ n = n.plus(Z.times(sqrt(z)))
657
+ if height is None:
658
+ h = fidw((point1.height, point2.height, point3.height),
659
+ map1(fabs, distance1, distance2, distance3))
660
+ else:
661
+ h = Height(height)
662
+ kwds = _xkwds(LatLon_and_kwds, height=h)
663
+ return n.toLatLon(**kwds) # Nvector(n.x, n.y, n.z).toLatLon(...)
664
+
665
+ # no intersection, d < EPS_2 or fabs(j) < EPS_2 or z < EPS
666
+ t = _SPACE_(_no_, _intersection_, NN)
667
+ raise IntersectionError(point1=point1, distance1=distance1,
668
+ point2=point2, distance2=distance2,
669
+ point3=point3, distance3=distance3,
670
+ txt=unstr(t, z=z, useZ=useZ, wrap=wrap))
671
+
672
+
673
+ __all__ += _ALL_DOCS(LatLonNvectorBase, NvectorBase, sumOf) # classes
674
+
675
+ # **) MIT License
676
+ #
677
+ # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
678
+ #
679
+ # Permission is hereby granted, free of charge, to any person obtaining a
680
+ # copy of this software and associated documentation files (the "Software"),
681
+ # to deal in the Software without restriction, including without limitation
682
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
683
+ # and/or sell copies of the Software, and to permit persons to whom the
684
+ # Software is furnished to do so, subject to the following conditions:
685
+ #
686
+ # The above copyright notice and this permission notice shall be included
687
+ # in all copies or substantial portions of the Software.
688
+ #
689
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
690
+ # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
691
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
692
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
693
+ # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
694
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
695
+ # OTHER DEALINGS IN THE SOFTWARE.