pygeodesy 24.6.1__py2.py3-none-any.whl → 24.6.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.6.1.dist-info → PyGeodesy-24.6.24.dist-info}/METADATA +2 -2
- PyGeodesy-24.6.24.dist-info/RECORD +117 -0
- pygeodesy/__init__.py +33 -32
- pygeodesy/albers.py +2 -2
- pygeodesy/auxilats/__init__.py +1 -1
- pygeodesy/auxilats/auxAngle.py +40 -39
- pygeodesy/auxilats/auxDLat.py +3 -2
- pygeodesy/auxilats/auxLat.py +16 -18
- pygeodesy/auxilats/auxily.py +1 -1
- pygeodesy/azimuthal.py +10 -10
- pygeodesy/basics.py +9 -1
- pygeodesy/booleans.py +53 -66
- pygeodesy/cartesianBase.py +143 -155
- pygeodesy/css.py +14 -18
- pygeodesy/datums.py +6 -6
- pygeodesy/deprecated/__init__.py +1 -1
- pygeodesy/deprecated/classes.py +16 -2
- pygeodesy/deprecated/datum.py +3 -3
- pygeodesy/deprecated/functions.py +6 -8
- pygeodesy/dms.py +23 -27
- pygeodesy/ecef.py +49 -55
- pygeodesy/elevations.py +4 -4
- pygeodesy/ellipsoidalBase.py +28 -70
- pygeodesy/ellipsoidalBaseDI.py +19 -23
- pygeodesy/ellipsoidalExact.py +3 -3
- pygeodesy/ellipsoidalGeodSolve.py +15 -23
- pygeodesy/ellipsoidalKarney.py +37 -60
- pygeodesy/ellipsoidalNvector.py +44 -50
- pygeodesy/ellipsoidalVincenty.py +11 -14
- pygeodesy/ellipsoids.py +107 -101
- pygeodesy/errors.py +101 -49
- pygeodesy/etm.py +32 -44
- pygeodesy/formy.py +55 -58
- pygeodesy/frechet.py +20 -23
- pygeodesy/fsums.py +4 -4
- pygeodesy/gars.py +3 -4
- pygeodesy/geodesici.py +909 -0
- pygeodesy/geodesicw.py +11 -13
- pygeodesy/geodesicx/__init__.py +4 -4
- pygeodesy/geodesicx/gx.py +18 -28
- pygeodesy/geodesicx/gxbases.py +20 -8
- pygeodesy/geodesicx/gxline.py +16 -22
- pygeodesy/geodsolve.py +102 -34
- pygeodesy/geohash.py +39 -60
- pygeodesy/geoids.py +28 -37
- pygeodesy/hausdorff.py +21 -23
- pygeodesy/heights.py +15 -28
- pygeodesy/internals.py +19 -12
- pygeodesy/interns.py +4 -10
- pygeodesy/iters.py +2 -2
- pygeodesy/karney.py +20 -4
- pygeodesy/ktm.py +13 -16
- pygeodesy/latlonBase.py +202 -191
- pygeodesy/lazily.py +96 -59
- pygeodesy/lcc.py +29 -32
- pygeodesy/ltp.py +43 -24
- pygeodesy/ltpTuples.py +190 -183
- pygeodesy/mgrs.py +35 -9
- pygeodesy/named.py +106 -72
- pygeodesy/namedTuples.py +43 -14
- pygeodesy/nvectorBase.py +23 -27
- pygeodesy/osgr.py +9 -9
- pygeodesy/points.py +7 -7
- pygeodesy/rhumb/__init__.py +1 -1
- pygeodesy/rhumb/aux_.py +5 -5
- pygeodesy/rhumb/bases.py +30 -31
- pygeodesy/rhumb/ekx.py +3 -4
- pygeodesy/rhumb/solve.py +8 -61
- pygeodesy/solveBase.py +22 -19
- pygeodesy/sphericalBase.py +26 -21
- pygeodesy/sphericalNvector.py +13 -13
- pygeodesy/sphericalTrigonometry.py +86 -97
- pygeodesy/streprs.py +8 -36
- pygeodesy/trf.py +3 -3
- pygeodesy/triaxials.py +117 -91
- pygeodesy/units.py +229 -321
- pygeodesy/unitsBase.py +116 -108
- pygeodesy/ups.py +26 -31
- pygeodesy/utily.py +12 -11
- pygeodesy/utm.py +35 -40
- pygeodesy/utmups.py +43 -46
- pygeodesy/utmupsBase.py +9 -10
- pygeodesy/vector3d.py +59 -62
- pygeodesy/vector3dBase.py +17 -15
- pygeodesy/webmercator.py +19 -21
- pygeodesy/wgrs.py +18 -20
- PyGeodesy-24.6.1.dist-info/RECORD +0 -116
- {PyGeodesy-24.6.1.dist-info → PyGeodesy-24.6.24.dist-info}/WHEEL +0 -0
- {PyGeodesy-24.6.1.dist-info → PyGeodesy-24.6.24.dist-info}/top_level.txt +0 -0
pygeodesy/cartesianBase.py
CHANGED
|
@@ -16,34 +16,34 @@ from pygeodesy.constants import EPS, EPS0, INT0, PI2, _isfinite, isnear0, \
|
|
|
16
16
|
from pygeodesy.datums import Datum, _earth_ellipsoid, _spherical_datum, \
|
|
17
17
|
Transform, _WGS84, _xinstanceof
|
|
18
18
|
# from pygeodesy.ecef import EcefKarney # _MODS
|
|
19
|
-
from pygeodesy.errors import _IsnotError, _TypeError, _ValueError, \
|
|
20
|
-
_xdatum, _xkwds
|
|
19
|
+
from pygeodesy.errors import _IsnotError, _TypeError, _ValueError, _xattr, \
|
|
20
|
+
_xdatum, _xkwds, _xkwds_get, _xkwds_pop2
|
|
21
21
|
from pygeodesy.fmath import cbrt, hypot, hypot_, hypot2, fabs, sqrt # hypot
|
|
22
22
|
# from pygeodesy.formy import _hartzell # _MODS
|
|
23
23
|
from pygeodesy.fsums import fsumf_, Fmt
|
|
24
|
-
from pygeodesy.interns import _COMMASPACE_, _phi_
|
|
24
|
+
from pygeodesy.interns import _COMMASPACE_, _datum_, _no_, _phi_
|
|
25
25
|
from pygeodesy.interns import _ellipsoidal_, _spherical_ # PYCHOK used!
|
|
26
26
|
from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS
|
|
27
|
-
from pygeodesy.named import
|
|
28
|
-
from pygeodesy.namedTuples import LatLon4Tuple,
|
|
29
|
-
Bearing2Tuple # PYCHOK .sphericalBase
|
|
27
|
+
from pygeodesy.named import _name2__, _Pass
|
|
28
|
+
from pygeodesy.namedTuples import LatLon4Tuple, _NamedTupleTo , Vector3Tuple, \
|
|
29
|
+
Vector4Tuple, Bearing2Tuple # PYCHOK .sphericalBase
|
|
30
30
|
# from pygeodesy.nvectorBase import _N_vector # _MODS
|
|
31
31
|
from pygeodesy.props import deprecated_method, Property, Property_RO, \
|
|
32
32
|
property_doc_, property_RO, _update_all
|
|
33
|
-
# from pygeodesy
|
|
33
|
+
# from pygeodesy,resections import cassini, collins5, pierlot, pierlotx, \
|
|
34
|
+
# tienstra7 # _MODS
|
|
34
35
|
# from pygeodesy.streprs import Fmt # from .fsums
|
|
35
36
|
# from pygeodesy.triaxials import Triaxial_ # _MODS
|
|
36
|
-
from pygeodesy.units import Degrees, Height, _heigHt, _isMeter, Meter,
|
|
37
|
-
Radians, _toDegrees, _toRadians
|
|
37
|
+
from pygeodesy.units import Degrees, Height, _heigHt, _isMeter, Meter, Radians
|
|
38
38
|
from pygeodesy.utily import acos1, sincos2d, sincos2_, atan2, degrees, radians
|
|
39
|
-
from pygeodesy.vector3d import Vector3d,
|
|
39
|
+
from pygeodesy.vector3d import Vector3d, _xyzhdlln4
|
|
40
40
|
# from pygeodesy.vector3dBase import _xyz3 # _MODS
|
|
41
|
-
# from pygeodesy import ltp
|
|
41
|
+
# from pygeodesy import ltp # _MODS
|
|
42
42
|
|
|
43
43
|
# from math import atan2, degrees, fabs, radians, sqrt # from .fmath, .utily
|
|
44
44
|
|
|
45
45
|
__all__ = _ALL_LAZY.cartesianBase
|
|
46
|
-
__version__ = '24.
|
|
46
|
+
__version__ = '24.06.11'
|
|
47
47
|
|
|
48
48
|
_r_ = 'r'
|
|
49
49
|
_theta_ = 'theta'
|
|
@@ -55,26 +55,25 @@ class CartesianBase(Vector3d):
|
|
|
55
55
|
_datum = None # L{Datum}, to be overriden
|
|
56
56
|
_height = None # height (L{Height}), set or approximated
|
|
57
57
|
|
|
58
|
-
def __init__(self, x_xyz, y=None, z=None, datum=None,
|
|
58
|
+
def __init__(self, x_xyz, y=None, z=None, datum=None, **ll_name):
|
|
59
59
|
'''New C{Cartesian...}.
|
|
60
60
|
|
|
61
61
|
@arg x_xyz: Cartesian X coordinate (C{scalar}) or a C{Cartesian},
|
|
62
62
|
L{Ecef9Tuple}, L{Vector3Tuple} or L{Vector4Tuple}.
|
|
63
63
|
@kwarg y: Cartesian Y coordinate (C{scalar}), ignored if B{C{x_xyz}}
|
|
64
64
|
is not C{scalar}, otherwise same units as B{C{x_xyz}}.
|
|
65
|
-
@kwarg z: Cartesian Z coordinate (C{scalar}),
|
|
66
|
-
is not C{scalar}, otherwise same units as B{C{x_xyz}}.
|
|
65
|
+
@kwarg z: Cartesian Z coordinate (C{scalar}), like B{C{y}}.
|
|
67
66
|
@kwarg datum: Optional datum (L{Datum}, L{Ellipsoid}, L{Ellipsoid2}
|
|
68
67
|
or L{a_f2Tuple}).
|
|
69
|
-
@kwarg
|
|
70
|
-
|
|
68
|
+
@kwarg ll_name: Optional C{B{name}=NN} (C{str}) and optional, original
|
|
69
|
+
latlon C{B{ll}=None} (C{LatLon}).
|
|
71
70
|
|
|
72
71
|
@raise TypeError: Non-scalar B{C{x_xyz}}, B{C{y}} or B{C{z}} coordinate
|
|
73
72
|
or B{C{x_xyz}} not a C{Cartesian}, L{Ecef9Tuple},
|
|
74
73
|
L{Vector3Tuple} or L{Vector4Tuple} or B{C{datum}} is
|
|
75
74
|
not a L{Datum}.
|
|
76
75
|
'''
|
|
77
|
-
h, d, n =
|
|
76
|
+
h, d, ll, n = _xyzhdlln4(x_xyz, None, datum, **ll_name)
|
|
78
77
|
Vector3d.__init__(self, x_xyz, y=y, z=z, ll=ll, name=n)
|
|
79
78
|
if h is not None:
|
|
80
79
|
self._height = Height(h)
|
|
@@ -113,7 +112,7 @@ class CartesianBase(Vector3d):
|
|
|
113
112
|
|
|
114
113
|
@see: Function L{pygeodesy.cassini} for references and more details.
|
|
115
114
|
'''
|
|
116
|
-
return
|
|
115
|
+
return _MODS.resections.cassini(self, pointB, pointC, alpha, beta,
|
|
117
116
|
useZ=useZ, datum=self.datum)
|
|
118
117
|
|
|
119
118
|
@deprecated_method
|
|
@@ -149,7 +148,7 @@ class CartesianBase(Vector3d):
|
|
|
149
148
|
|
|
150
149
|
@see: Function L{pygeodesy.collins5} for references and more details.
|
|
151
150
|
'''
|
|
152
|
-
return
|
|
151
|
+
return _MODS.resections.collins5(self, pointB, pointC, alpha, beta,
|
|
153
152
|
useZ=useZ, datum=self.datum)
|
|
154
153
|
|
|
155
154
|
@deprecated_method
|
|
@@ -180,29 +179,37 @@ class CartesianBase(Vector3d):
|
|
|
180
179
|
_update_all(self)
|
|
181
180
|
self._datum = d
|
|
182
181
|
|
|
183
|
-
def destinationXyz(self, delta, Cartesian=None, **
|
|
182
|
+
def destinationXyz(self, delta, Cartesian=None, **name_Cartesian_kwds):
|
|
184
183
|
'''Calculate the destination using a I{local} delta from this cartesian.
|
|
185
184
|
|
|
186
|
-
@arg delta: Local delta to the destination (L{XyzLocal}, L{Enu},
|
|
187
|
-
|
|
188
|
-
@kwarg Cartesian: Optional (geocentric) class to return the
|
|
189
|
-
|
|
190
|
-
@kwarg
|
|
191
|
-
|
|
185
|
+
@arg delta: Local delta to the destination (L{XyzLocal}, L{Enu}, L{Ned}
|
|
186
|
+
or L{Local9Tuple}).
|
|
187
|
+
@kwarg Cartesian: Optional (geocentric) class to return the destination
|
|
188
|
+
or C{None}.
|
|
189
|
+
@kwarg name_Cartesian_kwds: Optional C{B{name}=NN} (C{str}) and optional,
|
|
190
|
+
additional B{C{Cartesian}} keyword arguments, ignored if
|
|
191
|
+
C{B{Cartesian} is None}.
|
|
192
192
|
|
|
193
193
|
@return: Destination as a C{B{Cartesian}(x, y, z, **B{Cartesian_kwds})}
|
|
194
194
|
instance or if C{B{Cartesian} is None}, an L{Ecef9Tuple}C{(x, y,
|
|
195
195
|
z, lat, lon, height, C, M, datum)} with C{M=None} always.
|
|
196
196
|
|
|
197
|
-
@raise TypeError: Invalid B{C{delta}}, B{C{Cartesian}} or
|
|
198
|
-
|
|
197
|
+
@raise TypeError: Invalid B{C{delta}}, B{C{Cartesian}} or B{C{Cartesian_kwds}}
|
|
198
|
+
item or C{datum} missing or incompatible.
|
|
199
199
|
'''
|
|
200
|
+
n, kwds = _name2__(name_Cartesian_kwds, _or_nameof=self)
|
|
200
201
|
if Cartesian is None:
|
|
201
202
|
r = self._Ltp._local2ecef(delta, nine=True)
|
|
202
203
|
else:
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
204
|
+
d = self.datum
|
|
205
|
+
if not d:
|
|
206
|
+
raise _TypeError(delta=delta, txt=_no_(_datum_))
|
|
207
|
+
t = _xkwds_get(kwds, datum=d)
|
|
208
|
+
if _xattr(t, ellipsoid=None) != d.ellipsoid:
|
|
209
|
+
raise _TypeError(datum=t, txt=str(d))
|
|
210
|
+
c = self._Ltp._local2ecef(delta, nine=False)
|
|
211
|
+
r = Cartesian(*c, **kwds)
|
|
212
|
+
return r.renamed(n) if n else r
|
|
206
213
|
|
|
207
214
|
@property_RO
|
|
208
215
|
def Ecef(self):
|
|
@@ -379,13 +386,13 @@ class CartesianBase(Vector3d):
|
|
|
379
386
|
def isEllipsoidal(self):
|
|
380
387
|
'''Check whether this cartesian is ellipsoidal (C{bool} or C{None} if unknown).
|
|
381
388
|
'''
|
|
382
|
-
return self.datum
|
|
389
|
+
return _xattr(self.datum, isEllipsoidal=None)
|
|
383
390
|
|
|
384
391
|
@Property_RO
|
|
385
392
|
def isSpherical(self):
|
|
386
393
|
'''Check whether this cartesian is spherical (C{bool} or C{None} if unknown).
|
|
387
394
|
'''
|
|
388
|
-
return self.datum
|
|
395
|
+
return _xattr(self.datum, isSpherical=None)
|
|
389
396
|
|
|
390
397
|
@Property_RO
|
|
391
398
|
def latlon(self):
|
|
@@ -405,26 +412,19 @@ class CartesianBase(Vector3d):
|
|
|
405
412
|
'''
|
|
406
413
|
return self.toEcef().latlonheightdatum
|
|
407
414
|
|
|
408
|
-
@property_RO
|
|
409
|
-
def _ltp(self):
|
|
410
|
-
'''(INTERNAL) Get module C{.ltp}, I{once}.
|
|
411
|
-
'''
|
|
412
|
-
CartesianBase._ltp = m = _MODS.ltp # overwrite property_RO
|
|
413
|
-
return m
|
|
414
|
-
|
|
415
415
|
@Property_RO
|
|
416
416
|
def _Ltp(self):
|
|
417
417
|
'''(INTERNAL) Cache for L{toLtp}.
|
|
418
418
|
'''
|
|
419
|
-
return
|
|
419
|
+
return _MODS.ltp.Ltp(self._ecef9, ecef=self.Ecef(self.datum), name=self.name)
|
|
420
420
|
|
|
421
421
|
@Property_RO
|
|
422
422
|
def _N_vector(self):
|
|
423
423
|
'''(INTERNAL) Get the (C{nvectorBase._N_vector_}).
|
|
424
424
|
'''
|
|
425
|
-
|
|
425
|
+
_N = _MODS.nvectorBase._N_vector_
|
|
426
426
|
x, y, z, h = self._n_xyzh4(self.datum)
|
|
427
|
-
return
|
|
427
|
+
return _N(x, y, z, h=h, name=self.name)
|
|
428
428
|
|
|
429
429
|
def _n_xyzh4(self, datum):
|
|
430
430
|
'''(INTERNAL) Get the n-vector components as L{Vector4Tuple}.
|
|
@@ -516,7 +516,7 @@ class CartesianBase(Vector3d):
|
|
|
516
516
|
|
|
517
517
|
@see: Function L{pygeodesy.pierlot} for references and more details.
|
|
518
518
|
'''
|
|
519
|
-
return
|
|
519
|
+
return _MODS.resections.pierlot(self, point2, point3, alpha12, alpha23,
|
|
520
520
|
useZ=useZ, eps=eps, datum=self.datum)
|
|
521
521
|
|
|
522
522
|
def pierlotx(self, point2, point3, alpha1, alpha2, alpha3, useZ=False):
|
|
@@ -543,16 +543,9 @@ class CartesianBase(Vector3d):
|
|
|
543
543
|
|
|
544
544
|
@see: Function L{pygeodesy.pierlotx} for references and more details.
|
|
545
545
|
'''
|
|
546
|
-
return
|
|
546
|
+
return _MODS.resections.pierlotx(self, point2, point3, alpha1, alpha2, alpha3,
|
|
547
547
|
useZ=useZ, datum=self.datum)
|
|
548
548
|
|
|
549
|
-
@property_RO
|
|
550
|
-
def _resections(self):
|
|
551
|
-
'''(INTERNAL) Import the C{.resections} module, I{once}.
|
|
552
|
-
'''
|
|
553
|
-
CartesianBase._resections = m = _MODS.resections # overwrite property_RO
|
|
554
|
-
return m
|
|
555
|
-
|
|
556
549
|
def Roc2(self, earth=None):
|
|
557
550
|
'''Compute this cartesian's I{normal} and I{pseudo, z-based} radius of curvature.
|
|
558
551
|
|
|
@@ -624,7 +617,7 @@ class CartesianBase(Vector3d):
|
|
|
624
617
|
|
|
625
618
|
@see: Function L{pygeodesy.tienstra7} for references and more details.
|
|
626
619
|
'''
|
|
627
|
-
return
|
|
620
|
+
return _MODS.resections.tienstra7(self, pointB, pointC, alpha, beta, gamma,
|
|
628
621
|
useZ=useZ, datum=self.datum)
|
|
629
622
|
|
|
630
623
|
@deprecated_method
|
|
@@ -685,8 +678,8 @@ class CartesianBase(Vector3d):
|
|
|
685
678
|
if d == datum2:
|
|
686
679
|
return c.copy() if c is self else c
|
|
687
680
|
|
|
688
|
-
elif
|
|
689
|
-
|
|
681
|
+
elif d is None or (d.transform.isunity and
|
|
682
|
+
datum2.transform.isunity):
|
|
690
683
|
return c.dup(datum=datum2)
|
|
691
684
|
|
|
692
685
|
elif d == _WGS84:
|
|
@@ -717,15 +710,15 @@ class CartesianBase(Vector3d):
|
|
|
717
710
|
@kwarg datum: Optional datum (L{Datum}, L{Ellipsoid}, L{Ellipsoid2}
|
|
718
711
|
or L{a_f2Tuple}).
|
|
719
712
|
@kwarg height: Optional height, overriding the converted height
|
|
720
|
-
(C{meter}),
|
|
713
|
+
(C{meter}), only if C{B{LatLon} is not None}.
|
|
721
714
|
@kwarg LatLon: Optional class to return the geodetic point
|
|
722
715
|
(C{LatLon}) or C{None}.
|
|
723
716
|
@kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword
|
|
724
717
|
arguments, ignored if C{B{LatLon} is None}.
|
|
725
718
|
|
|
726
|
-
@return: The geodetic point (B{C{LatLon}}) or if B{
|
|
727
|
-
is
|
|
728
|
-
|
|
719
|
+
@return: The geodetic point (B{C{LatLon}}) or if C{B{LatLon}
|
|
720
|
+
is None}, an L{Ecef9Tuple}C{(x, y, z, lat, lon, height,
|
|
721
|
+
C, M, datum)} with C{C} and C{M} if available.
|
|
729
722
|
|
|
730
723
|
@raise TypeError: Invalid B{C{datum}} or B{C{LatLon_kwds}}.
|
|
731
724
|
'''
|
|
@@ -746,46 +739,45 @@ class CartesianBase(Vector3d):
|
|
|
746
739
|
def toLocal(self, Xyz=None, ltp=None, **Xyz_kwds):
|
|
747
740
|
'''Convert this I{geocentric} cartesian to I{local} C{X}, C{Y} and C{Z}.
|
|
748
741
|
|
|
749
|
-
@kwarg Xyz: Optional class to return C{X}, C{Y} and C{Z}
|
|
750
|
-
|
|
751
|
-
@kwarg ltp: The I{local tangent plane} (LTP) to use,
|
|
752
|
-
|
|
753
|
-
@kwarg Xyz_kwds: Optional, additional B{C{Xyz}} keyword
|
|
754
|
-
|
|
742
|
+
@kwarg Xyz: Optional class to return C{X}, C{Y} and C{Z} (L{XyzLocal},
|
|
743
|
+
L{Enu}, L{Ned}) or C{None}.
|
|
744
|
+
@kwarg ltp: The I{local tangent plane} (LTP) to use, overriding this
|
|
745
|
+
cartesian's LTP (L{Ltp}).
|
|
746
|
+
@kwarg Xyz_kwds: Optional, additional B{C{Xyz}} keyword arguments,
|
|
747
|
+
ignored if C{B{Xyz} is None}.
|
|
755
748
|
|
|
756
|
-
@return: An B{C{Xyz}} instance or
|
|
757
|
-
|
|
758
|
-
ltp, ecef, M)} with C{M=None} always.
|
|
749
|
+
@return: An B{C{Xyz}} instance or a L{Local9Tuple}C{(x, y, z, lat, lon,
|
|
750
|
+
height, ltp, ecef, M)} if C{B{Xyz} is None} (with C{M=None}).
|
|
759
751
|
|
|
760
752
|
@raise TypeError: Invalid B{C{ltp}}.
|
|
761
753
|
'''
|
|
762
|
-
|
|
763
|
-
return p._ecef2local(self._ecef9, Xyz, Xyz_kwds)
|
|
754
|
+
return _MODS.ltp._toLocal(self, ltp, Xyz, Xyz_kwds) # self._ecef9
|
|
764
755
|
|
|
765
|
-
def toLtp(self, Ecef=None):
|
|
756
|
+
def toLtp(self, Ecef=None, **name):
|
|
766
757
|
'''Return the I{local tangent plane} (LTP) for this cartesian.
|
|
767
758
|
|
|
768
759
|
@kwarg Ecef: Optional ECEF I{class} (L{EcefKarney}, ...
|
|
769
760
|
L{EcefYou}), overriding this cartesian's C{Ecef}.
|
|
761
|
+
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
770
762
|
'''
|
|
771
|
-
return self
|
|
772
|
-
self._ecef9, ecef=Ecef(self.datum), name=self.name)
|
|
763
|
+
return _MODS.ltp._toLtp(self, Ecef, self._ecef9, name) # self._Ltp
|
|
773
764
|
|
|
774
|
-
def toNvector(self, Nvector=None, datum=None, **
|
|
765
|
+
def toNvector(self, Nvector=None, datum=None, **name_Nvector_kwds):
|
|
775
766
|
'''Convert this cartesian to C{n-vector} components, I{including height}.
|
|
776
767
|
|
|
777
768
|
@kwarg Nvector: Optional class to return the C{n-vector} components
|
|
778
769
|
(C{Nvector}) or C{None}.
|
|
779
770
|
@kwarg datum: Optional datum (L{Datum}, L{Ellipsoid}, L{Ellipsoid2}
|
|
780
771
|
or L{a_f2Tuple}) overriding this cartesian's datum.
|
|
781
|
-
@kwarg
|
|
782
|
-
|
|
772
|
+
@kwarg name_Nvector_kwds: Optional C{B{name}=NN} (C{str}) and optional,
|
|
773
|
+
additional B{C{Nvector}} keyword arguments, ignored if
|
|
774
|
+
C{B{Nvector} is None}.
|
|
783
775
|
|
|
784
776
|
@return: An B{C{Nvector}} or a L{Vector4Tuple}C{(x, y, z, h)} if
|
|
785
|
-
B{
|
|
777
|
+
C{B{Nvector} is None}.
|
|
786
778
|
|
|
787
|
-
@raise TypeError: Invalid B{C{Nvector}}, B{C{
|
|
788
|
-
B{C{
|
|
779
|
+
@raise TypeError: Invalid B{C{Nvector}}, B{C{datum}} or
|
|
780
|
+
B{C{name_Nvector_kwds}} item.
|
|
789
781
|
|
|
790
782
|
@raise ValueError: B{C{Cartesian}} at origin.
|
|
791
783
|
'''
|
|
@@ -795,9 +787,13 @@ class CartesianBase(Vector3d):
|
|
|
795
787
|
if d != self.datum:
|
|
796
788
|
r = self._n_xyzh4(d)
|
|
797
789
|
|
|
798
|
-
if Nvector is
|
|
799
|
-
|
|
800
|
-
|
|
790
|
+
if Nvector is None:
|
|
791
|
+
n, _ = _name2__(name_Nvector_kwds, _or_nameof=self)
|
|
792
|
+
if n:
|
|
793
|
+
r = r.dup(name=n)
|
|
794
|
+
else:
|
|
795
|
+
kwds = _xkwds(name_Nvector_kwds, h=r.h, datum=d)
|
|
796
|
+
r = Nvector(r.x, r.y, r.z, **self._name1__(kwds))
|
|
801
797
|
return r
|
|
802
798
|
|
|
803
799
|
def toRtp(self):
|
|
@@ -854,15 +850,15 @@ class CartesianBase(Vector3d):
|
|
|
854
850
|
arguments, ignored if C{B{Vector} is None}.
|
|
855
851
|
|
|
856
852
|
@return: A B{C{Vector}} or a L{Vector3Tuple}C{(x, y, z)} if
|
|
857
|
-
B{
|
|
853
|
+
C{B{Vector} is None}.
|
|
858
854
|
|
|
859
855
|
@raise TypeError: Invalid B{C{Vector}} or B{C{Vector_kwds}}.
|
|
860
856
|
'''
|
|
861
|
-
return self.xyz if Vector is None else
|
|
862
|
-
|
|
857
|
+
return self.xyz if Vector is None else Vector(
|
|
858
|
+
self.x, self.y, self.z, **self._name1__(Vector_kwds))
|
|
863
859
|
|
|
864
860
|
|
|
865
|
-
class RadiusThetaPhi3Tuple(
|
|
861
|
+
class RadiusThetaPhi3Tuple(_NamedTupleTo):
|
|
866
862
|
'''3-Tuple C{(r, theta, phi)} with radial distance C{r} in C{meter}, inclination
|
|
867
863
|
C{theta} (with respect to the positive z-axis) and azimuthal angle C{phi} in
|
|
868
864
|
L{Degrees} I{or} L{Radians} representing a U{spherical, polar position
|
|
@@ -885,89 +881,87 @@ class RadiusThetaPhi3Tuple(_NamedTuple):
|
|
|
885
881
|
@see: Function L{rtp2xyz_}.
|
|
886
882
|
'''
|
|
887
883
|
r, t, p = self
|
|
888
|
-
t, p, _ =
|
|
889
|
-
|
|
890
|
-
return rtp2xyz_(r, t, p, name=n, **kwds)
|
|
884
|
+
t, p, _ = _NamedTupleTo._Radians3(self, t, p)
|
|
885
|
+
return rtp2xyz_(r, t, p, **name_Cartesian_and_kwds)
|
|
891
886
|
|
|
892
887
|
def toDegrees(self, **name):
|
|
893
888
|
'''Convert this L{RadiusThetaPhi3Tuple}'s angles to L{Degrees}.
|
|
894
889
|
|
|
895
|
-
@kwarg name: Optional name (C{str}), overriding this name.
|
|
890
|
+
@kwarg name: Optional C{B{name}=NN} (C{str}), overriding this name.
|
|
896
891
|
|
|
897
892
|
@return: L{RadiusThetaPhi3Tuple}C{(r, theta, phi)} with C{theta}
|
|
898
893
|
and C{phi} both in L{Degrees}.
|
|
899
894
|
'''
|
|
900
|
-
|
|
901
|
-
t, p, _ = _toDegrees(self, t, p)
|
|
902
|
-
return _ or self.classof(r, Degrees(theta=t), Degrees(phi=p),
|
|
903
|
-
name=_name__(name, _or_nameof=self))
|
|
895
|
+
return self._toX3U(_NamedTupleTo._Degrees3, Degrees, name)
|
|
904
896
|
|
|
905
897
|
def toRadians(self, **name):
|
|
906
898
|
'''Convert this L{RadiusThetaPhi3Tuple}'s angles to L{Radians}.
|
|
907
899
|
|
|
908
|
-
@kwarg name: Optional
|
|
900
|
+
@kwarg name: Optional C{B{name}=NN} (C{str}), overriding this name.
|
|
909
901
|
|
|
910
|
-
@return: L{RadiusThetaPhi3Tuple}C{(r, theta, phi)} with
|
|
911
|
-
|
|
902
|
+
@return: L{RadiusThetaPhi3Tuple}C{(r, theta, phi)} with C{theta}
|
|
903
|
+
and C{phi} both in L{Radians}.
|
|
912
904
|
'''
|
|
913
|
-
|
|
914
|
-
t, p, _ = _toRadians(self, t, p)
|
|
915
|
-
return _ or self.classof(r, Radians(theta=t), Radians(phi=p),
|
|
916
|
-
name=_name__(name, _or_nameof=self))
|
|
905
|
+
return self._toX3U(_NamedTupleTo._Radians3, Radians, name)
|
|
917
906
|
|
|
907
|
+
def _toU(self, U):
|
|
908
|
+
M = RadiusThetaPhi3Tuple._Units_[0] # Meter
|
|
909
|
+
return self.reUnit(M, U, U).toUnits()
|
|
918
910
|
|
|
919
|
-
def
|
|
920
|
-
|
|
911
|
+
def _toX3U(self, _X3, U, name):
|
|
912
|
+
r, t, p = self
|
|
913
|
+
t, p, s = _X3(self, t, p)
|
|
914
|
+
if s is None or name:
|
|
915
|
+
n = self._name__(name)
|
|
916
|
+
s = self.classof(r, t, p, name=n)._toU(U)
|
|
917
|
+
return s
|
|
921
918
|
|
|
922
|
-
@arg r_rtp: Radial distance (C{scalar}, conventially C{meter}) or a previous
|
|
923
|
-
L{RadiusThetaPhi3Tuple} instance.
|
|
924
|
-
@arg theta_phi: Inclination B{C{theta}} (C{degrees} with respect to the positive z-axis)
|
|
925
|
-
and azimuthal angle B{C{phi}} (C{degrees}), ignored if C{B{r_rtp}} is a
|
|
926
|
-
L{RadiusThetaPhi3Tuple}.
|
|
927
|
-
@kwarg name_Cartesian_and_kwds: Optional C{B{name}=NN} (C{str}), a C{B{Cartesian}=None}
|
|
928
|
-
class to return the coordinates and additional C{B{Cartesian}}
|
|
929
|
-
keyword arguments.
|
|
930
919
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
units as radius C{r}, C{meter} conventionally.
|
|
920
|
+
def rtp2xyz(r_rtp, theta=0, phi=0, **name_Cartesian_and_kwds):
|
|
921
|
+
'''Convert I{spherical, polar} C{(r, theta, phi)} to cartesian C{(x, y, z)} coordinates.
|
|
934
922
|
|
|
935
|
-
@
|
|
923
|
+
@arg theta: Inclination B{C{theta}} (C{degrees} with respect to the positive z-axis),
|
|
924
|
+
required if C{B{r_rtp}} is C{scalar}, ignored otherwise.
|
|
925
|
+
@arg phi: Azimuthal angle B{C{phi}} (C{degrees}), like B{C{theta}}.
|
|
936
926
|
|
|
937
|
-
@see:
|
|
927
|
+
@see: Function L{rtp2xyz_} for further details.
|
|
938
928
|
'''
|
|
939
929
|
if isinstance(r_rtp, RadiusThetaPhi3Tuple):
|
|
940
930
|
c = r_rtp.toCartesian(**name_Cartesian_and_kwds)
|
|
941
931
|
else:
|
|
942
|
-
c = rtp2xyz_(r_rtp,
|
|
932
|
+
c = rtp2xyz_(r_rtp, radians(theta), radians(phi), **name_Cartesian_and_kwds)
|
|
943
933
|
return c
|
|
944
934
|
|
|
945
935
|
|
|
946
|
-
def rtp2xyz_(r_rtp,
|
|
936
|
+
def rtp2xyz_(r_rtp, theta=0, phi=0, **name_Cartesian_and_kwds):
|
|
947
937
|
'''Convert I{spherical, polar} C{(r, theta, phi)} to cartesian C{(x, y, z)} coordinates.
|
|
948
938
|
|
|
949
939
|
@arg r_rtp: Radial distance (C{scalar}, conventially C{meter}) or a previous
|
|
950
940
|
L{RadiusThetaPhi3Tuple} instance.
|
|
951
|
-
@arg
|
|
952
|
-
|
|
953
|
-
|
|
941
|
+
@arg theta: Inclination B{C{theta}} (C{radians} with respect to the positive z-axis),
|
|
942
|
+
required if C{B{r_rtp}} is C{scalar}, ignored otherwise.
|
|
943
|
+
@arg phi: Azimuthal angle B{C{phi}} (C{radians}), like B{C{theta}}.
|
|
954
944
|
@kwarg name_Cartesian_and_kwds: Optional C{B{name}=NN} (C{str}), a C{B{Cartesian}=None}
|
|
955
|
-
|
|
956
|
-
|
|
945
|
+
class to return the coordinates and optional, additional C{B{Cartesian}}
|
|
946
|
+
keyword arguments.
|
|
957
947
|
|
|
958
948
|
@return: A C{B{Cartesian}(x, y, z)} instance or if no C{B{Cartesian}} keyword argument
|
|
959
949
|
is given a L{Vector3Tuple}C{(x, y, z)}, with C{x}, C{y} and C{z} in the same
|
|
960
950
|
units as radius C{r}, C{meter} conventionally.
|
|
961
951
|
|
|
962
|
-
@raise TypeError: Invalid B{C{r_rtp}}
|
|
952
|
+
@raise TypeError: Invalid B{C{r_rtp}}, B{C{theta}}, B{C{phi}} or
|
|
953
|
+
B{C{name_Cartesian_and_kwds}} item.
|
|
963
954
|
|
|
964
955
|
@see: U{Physics convention<https://WikiPedia.org/wiki/Spherical_coordinate_system>}
|
|
965
|
-
(ISO 80000-2:2019)
|
|
956
|
+
(ISO 80000-2:2019), class L{RadiusThetaPhi3Tuple} and functions L{rtp2xyz}
|
|
957
|
+
and L{xyz2rtp}.
|
|
966
958
|
'''
|
|
967
|
-
if
|
|
959
|
+
if isinstance(r_rtp, RadiusThetaPhi3Tuple):
|
|
960
|
+
c = r_rtp.toCartesian(**name_Cartesian_and_kwds)
|
|
961
|
+
elif _isMeter(r_rtp):
|
|
968
962
|
r = r_rtp
|
|
969
963
|
if r and _isfinite(r):
|
|
970
|
-
s, z, y, x = sincos2_(
|
|
964
|
+
s, z, y, x = sincos2_(theta, phi)
|
|
971
965
|
s *= r
|
|
972
966
|
z *= r
|
|
973
967
|
y *= s
|
|
@@ -975,22 +969,16 @@ def rtp2xyz_(r_rtp, *theta_phi, **name_Cartesian_and_kwds):
|
|
|
975
969
|
else:
|
|
976
970
|
x = y = z = r
|
|
977
971
|
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
return n, Cartesian, kwds
|
|
981
|
-
|
|
982
|
-
n, C, kwds = _n_C_kwds3(**name_Cartesian_and_kwds)
|
|
972
|
+
n, kwds = _name2__(**name_Cartesian_and_kwds)
|
|
973
|
+
C, kwds = _xkwds_pop2(kwds, Cartesian=None)
|
|
983
974
|
c = Vector3Tuple(x, y, z, name=n) if C is None else \
|
|
984
975
|
C(x, y, z, name=n, **kwds)
|
|
985
|
-
|
|
986
|
-
elif isinstance(r_rtp, RadiusThetaPhi3Tuple):
|
|
987
|
-
c = r_rtp.toCartesian(**name_Cartesian_and_kwds)
|
|
988
976
|
else:
|
|
989
|
-
raise _TypeError(r_rtp=r_rtp,
|
|
977
|
+
raise _TypeError(r_rtp=r_rtp, theta=theta, phi=phi)
|
|
990
978
|
return c
|
|
991
979
|
|
|
992
980
|
|
|
993
|
-
def _rtp3(where,
|
|
981
|
+
def _rtp3(where, U, *x_y_z, **name):
|
|
994
982
|
'''(INTERNAL) Helper for C{.toRtp}, C{xyz2rtp} and C{xyz2rtp_}.
|
|
995
983
|
'''
|
|
996
984
|
x, y, z = _MODS.vector3dBase._xyz3(where, *x_y_z)
|
|
@@ -1000,43 +988,43 @@ def _rtp3(where, Unit, *x_y_z, **name):
|
|
|
1000
988
|
p = atan2(y, x)
|
|
1001
989
|
while p < 0:
|
|
1002
990
|
p += PI2
|
|
1003
|
-
if
|
|
991
|
+
if U is Degrees:
|
|
1004
992
|
t = degrees(t)
|
|
1005
993
|
p = degrees(p)
|
|
1006
994
|
else:
|
|
1007
995
|
t = p = _0_0
|
|
1008
|
-
return RadiusThetaPhi3Tuple(r,
|
|
996
|
+
return RadiusThetaPhi3Tuple(r, t, p, **name)._toU(U)
|
|
1009
997
|
|
|
1010
998
|
|
|
1011
|
-
def xyz2rtp(x_xyz,
|
|
999
|
+
def xyz2rtp(x_xyz, y=0, z=0, **name):
|
|
1012
1000
|
'''Convert cartesian C{(x, y, z)} to I{spherical, polar} C{(r, theta, phi)} coordinates.
|
|
1013
1001
|
|
|
1014
|
-
@return: L{RadiusThetaPhi3Tuple}C{(r, theta, phi)} with C{theta} and C{phi},
|
|
1015
|
-
|
|
1002
|
+
@return: L{RadiusThetaPhi3Tuple}C{(r, theta, phi)} with C{theta} and C{phi}, both
|
|
1003
|
+
in L{Degrees}.
|
|
1016
1004
|
|
|
1017
|
-
@see: Function L{xyz2rtp_}
|
|
1005
|
+
@see: Function L{xyz2rtp_} for further details.
|
|
1018
1006
|
'''
|
|
1019
|
-
return _rtp3(xyz2rtp, Degrees, x_xyz,
|
|
1007
|
+
return _rtp3(xyz2rtp, Degrees, x_xyz, y, z, **name)
|
|
1020
1008
|
|
|
1021
1009
|
|
|
1022
|
-
def xyz2rtp_(x_xyz,
|
|
1010
|
+
def xyz2rtp_(x_xyz, y=0, z=0, **name):
|
|
1023
1011
|
'''Convert cartesian C{(x, y, z)} to I{spherical, polar} C{(r, theta, phi)} coordinates.
|
|
1024
1012
|
|
|
1025
1013
|
@arg x_xyz: X component (C{scalar}) or a cartesian (C{Cartesian}, L{Ecef9Tuple},
|
|
1026
|
-
C{Nvector}, L{Vector3d}, L{Vector3Tuple}, L{Vector4Tuple} or a
|
|
1027
|
-
C{
|
|
1028
|
-
@arg
|
|
1014
|
+
C{Nvector}, L{Vector3d}, L{Vector3Tuple}, L{Vector4Tuple} or a C{tuple} or
|
|
1015
|
+
C{list} of 3+ C{scalar} items) if no C{y_z} specified.
|
|
1016
|
+
@arg y: Y component (C{scalar}), required if C{B{x_xyz}} is C{scalar}, ignored otherwise.
|
|
1017
|
+
@arg z: Z component (C{scalar}), like B{C{y}}.
|
|
1029
1018
|
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
1030
1019
|
|
|
1031
|
-
@return: L{RadiusThetaPhi3Tuple}C{(r, theta, phi)} with radial distance C{r}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
both in L{Radians}.
|
|
1020
|
+
@return: L{RadiusThetaPhi3Tuple}C{(r, theta, phi)} with radial distance C{r} (C{meter},
|
|
1021
|
+
same units as C{x}, C{y} and C{z}), inclination C{theta} (with respect to the
|
|
1022
|
+
positive z-axis) and azimuthal angle C{phi}, both in L{Radians}.
|
|
1035
1023
|
|
|
1036
1024
|
@see: U{Physics convention<https://WikiPedia.org/wiki/Spherical_coordinate_system>}
|
|
1037
|
-
(ISO 80000-2:2019).
|
|
1025
|
+
(ISO 80000-2:2019), class L{RadiusThetaPhi3Tuple} and function L{xyz2rtp}.
|
|
1038
1026
|
'''
|
|
1039
|
-
return _rtp3(xyz2rtp_, Radians, x_xyz,
|
|
1027
|
+
return _rtp3(xyz2rtp_, Radians, x_xyz, y, z, **name)
|
|
1040
1028
|
|
|
1041
1029
|
|
|
1042
1030
|
__all__ += _ALL_DOCS(CartesianBase)
|
pygeodesy/css.py
CHANGED
|
@@ -33,7 +33,7 @@ from pygeodesy.units import Bearing, Degrees, Easting, Height, _heigHt, \
|
|
|
33
33
|
# from math import fabs # from .karney
|
|
34
34
|
|
|
35
35
|
__all__ = _ALL_LAZY.css
|
|
36
|
-
__version__ = '24.
|
|
36
|
+
__version__ = '24.06.11'
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
def _CS0(cs0):
|
|
@@ -312,7 +312,7 @@ class CassiniSoldner(_NamedBase):
|
|
|
312
312
|
s, c = _sincos2d(m.lat1) # == self.lat0 == self.LatitudeOrigin()
|
|
313
313
|
self._sb0, self._cb0 = _norm2(s * g.f1, c)
|
|
314
314
|
|
|
315
|
-
def reverse(self, easting, northing, LatLon=None, **
|
|
315
|
+
def reverse(self, easting, northing, LatLon=None, **name_LatLon_kwds):
|
|
316
316
|
'''Convert a Cassini-Soldner location to (ellipsoidal) geodetic
|
|
317
317
|
lat- and longitude.
|
|
318
318
|
|
|
@@ -320,12 +320,11 @@ class CassiniSoldner(_NamedBase):
|
|
|
320
320
|
@arg northing: Northing of the location (C{meter}).
|
|
321
321
|
@kwarg LatLon: Optional, ellipsoidal class to return the geodetic
|
|
322
322
|
location as (C{LatLon}) or C{None}.
|
|
323
|
-
@kwarg
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
name (C{str}).
|
|
323
|
+
@kwarg name_LatLon_kwds: Optional name C{B{name}=NN} (C{str}) and
|
|
324
|
+
optional, additional B{C{LatLon}} keyword arguments,
|
|
325
|
+
ignored if C{B{LatLon} is None}.
|
|
327
326
|
|
|
328
|
-
@return: Geodetic location B{C{LatLon}} or if B{
|
|
327
|
+
@return: Geodetic location B{C{LatLon}} or if C{B{LatLon} is None},
|
|
329
328
|
a L{LatLon2Tuple}C{(lat, lon)}.
|
|
330
329
|
|
|
331
330
|
@raise CSSError: Ellipsoidal mismatch of B{C{LatLon}} and this projection.
|
|
@@ -335,7 +334,7 @@ class CassiniSoldner(_NamedBase):
|
|
|
335
334
|
@see: Method L{CassiniSoldner.reverse4}, L{CassiniSoldner.forward}.
|
|
336
335
|
L{CassiniSoldner.forward4} and L{CassiniSoldner.forward6}.
|
|
337
336
|
'''
|
|
338
|
-
n, kwds = _name2__(
|
|
337
|
+
n, kwds = _name2__(name_LatLon_kwds, _or_nameof=self)
|
|
339
338
|
r = self.reverse4(easting, northing, name=n)
|
|
340
339
|
if LatLon is None:
|
|
341
340
|
r = LatLon2Tuple(r.lat, r.lon, name=r.name) # PYCHOK expected
|
|
@@ -520,9 +519,8 @@ class Css(_NamedBase):
|
|
|
520
519
|
@kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword
|
|
521
520
|
arguments, ignored if C{B{LatLon} is None}.
|
|
522
521
|
|
|
523
|
-
@return: The geodetic point (B{C{LatLon}}) or if B{
|
|
524
|
-
|
|
525
|
-
datum)}.
|
|
522
|
+
@return: The geodetic point (B{C{LatLon}}) or if C{B{LatLon} is
|
|
523
|
+
None}, a L{LatLon4Tuple}C{(lat, lon, height, datum)}.
|
|
526
524
|
|
|
527
525
|
@raise TypeError: If B{C{LatLon}} or B{C{datum}} is not
|
|
528
526
|
ellipsoidal or invalid B{C{height}} or
|
|
@@ -610,15 +608,13 @@ def toCss(latlon, cs0=None, height=None, Css=Css, **name):
|
|
|
610
608
|
@arg latlon: Ellipsoidal point (C{LatLon} or L{LatLon4Tuple}).
|
|
611
609
|
@kwarg cs0: Optional, the Cassini-Soldner projection to use
|
|
612
610
|
(L{CassiniSoldner}).
|
|
613
|
-
@kwarg height: Optional height for the point, overriding the
|
|
614
|
-
|
|
615
|
-
@kwarg Css: Optional class to return the location (L{Css}) or
|
|
616
|
-
C{None}.
|
|
611
|
+
@kwarg height: Optional height for the point, overriding the default
|
|
612
|
+
height (C{meter}).
|
|
613
|
+
@kwarg Css: Optional class to return the location (L{Css}) or C{None}.
|
|
617
614
|
@kwarg name: Optional B{C{Css}} C{B{name}=NN} (C{str}).
|
|
618
615
|
|
|
619
|
-
@return: The Cassini-Soldner location (B{C{Css}}) or
|
|
620
|
-
L{EasNor3Tuple}C{(easting, northing, height)}
|
|
621
|
-
if B{C{Css}} is C{None}.
|
|
616
|
+
@return: The Cassini-Soldner location (B{C{Css}}) or if C{B{Css} is
|
|
617
|
+
None}, an L{EasNor3Tuple}C{(easting, northing, height)}.
|
|
622
618
|
|
|
623
619
|
@raise CSSError: Ellipsoidal mismatch of B{C{latlon}} and B{C{cs0}}.
|
|
624
620
|
|