pygeodesy 24.10.24__py2.py3-none-any.whl → 24.11.11__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.10.24.dist-info → PyGeodesy-24.11.11.dist-info}/METADATA +4 -4
- {PyGeodesy-24.10.24.dist-info → PyGeodesy-24.11.11.dist-info}/RECORD +54 -54
- {PyGeodesy-24.10.24.dist-info → PyGeodesy-24.11.11.dist-info}/WHEEL +1 -1
- pygeodesy/__init__.py +2 -2
- pygeodesy/azimuthal.py +51 -61
- pygeodesy/basics.py +34 -33
- pygeodesy/booleans.py +36 -36
- pygeodesy/cartesianBase.py +5 -5
- pygeodesy/constants.py +4 -4
- pygeodesy/css.py +7 -8
- pygeodesy/deprecated/__init__.py +1 -1
- pygeodesy/deprecated/classes.py +9 -9
- pygeodesy/deprecated/functions.py +6 -6
- pygeodesy/ecef.py +11 -14
- pygeodesy/ellipsoidalBase.py +105 -120
- pygeodesy/ellipsoidalBaseDI.py +114 -118
- pygeodesy/ellipsoidalExact.py +35 -37
- pygeodesy/ellipsoids.py +3 -3
- pygeodesy/errors.py +24 -24
- pygeodesy/etm.py +80 -72
- pygeodesy/fmath.py +39 -37
- pygeodesy/formy.py +3 -2
- pygeodesy/fsums.py +51 -40
- pygeodesy/geodesicw.py +15 -14
- pygeodesy/geodesicx/__init__.py +2 -2
- pygeodesy/geodsolve.py +7 -16
- pygeodesy/geoids.py +5 -5
- pygeodesy/heights.py +2 -2
- pygeodesy/internals.py +63 -79
- pygeodesy/karney.py +2 -2
- pygeodesy/ktm.py +11 -13
- pygeodesy/latlonBase.py +6 -6
- pygeodesy/lazily.py +5 -5
- pygeodesy/lcc.py +4 -4
- pygeodesy/ltp.py +10 -10
- pygeodesy/ltpTuples.py +74 -75
- pygeodesy/mgrs.py +9 -10
- pygeodesy/named.py +4 -0
- pygeodesy/osgr.py +9 -12
- pygeodesy/props.py +2 -2
- pygeodesy/resections.py +9 -10
- pygeodesy/rhumb/__init__.py +1 -1
- pygeodesy/rhumb/solve.py +3 -3
- pygeodesy/simplify.py +5 -5
- pygeodesy/sphericalNvector.py +80 -123
- pygeodesy/sphericalTrigonometry.py +60 -66
- pygeodesy/units.py +2 -2
- pygeodesy/utm.py +6 -6
- pygeodesy/vector2d.py +13 -13
- pygeodesy/vector3d.py +19 -21
- pygeodesy/vector3dBase.py +18 -15
- pygeodesy/webmercator.py +4 -4
- pygeodesy/wgrs.py +4 -4
- {PyGeodesy-24.10.24.dist-info → PyGeodesy-24.11.11.dist-info}/top_level.txt +0 -0
pygeodesy/ellipsoidalExact.py
CHANGED
|
@@ -24,7 +24,7 @@ from pygeodesy.points import _areaError, ispolar # PYCHOK exported
|
|
|
24
24
|
# from math import fabs # from .karney
|
|
25
25
|
|
|
26
26
|
__all__ = _ALL_LAZY.ellipsoidalExact
|
|
27
|
-
__version__ = '24.
|
|
27
|
+
__version__ = '24.11.06'
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
class Cartesian(CartesianEllipsoidalBase):
|
|
@@ -119,30 +119,28 @@ def areaOf(points, datum=_WGS84, wrap=True):
|
|
|
119
119
|
|
|
120
120
|
|
|
121
121
|
def intersection3(start1, end1, start2, end2, height=None, wrap=False, # was=True
|
|
122
|
-
|
|
123
|
-
'''I{Iteratively} compute the intersection point of two lines, each
|
|
124
|
-
|
|
125
|
-
initial bearing from North.
|
|
122
|
+
equidistant=None, tol=_TOL_M, **LatLon_and_kwds):
|
|
123
|
+
'''I{Iteratively} compute the intersection point of two geodesic lines, each
|
|
124
|
+
given as two points or as an start point and a bearing from North.
|
|
126
125
|
|
|
127
126
|
@arg start1: Start point of the first line (L{LatLon}).
|
|
128
127
|
@arg end1: End point of the first line (L{LatLon}) or the initial bearing
|
|
129
|
-
at
|
|
128
|
+
at B{C{start1}} (compass C{degrees360}).
|
|
130
129
|
@arg start2: Start point of the second line (L{LatLon}).
|
|
131
130
|
@arg end2: End point of the second line (L{LatLon}) or the initial bearing
|
|
132
|
-
at
|
|
131
|
+
at B{C{start2}} (compass C{degrees360}).
|
|
133
132
|
@kwarg height: Optional height at the intersection (C{meter}, conventionally)
|
|
134
133
|
or C{None} for the mean height.
|
|
135
|
-
@kwarg wrap: If C{True}, wrap or I{normalize} and unroll
|
|
136
|
-
|
|
134
|
+
@kwarg wrap: If C{True}, wrap or I{normalize} and unroll B{C{start2}} and
|
|
135
|
+
both B{C{end*}} points (C{bool}).
|
|
137
136
|
@kwarg equidistant: An azimuthal equidistant projection (I{class} or function
|
|
138
137
|
L{pygeodesy.equidistant}) or C{None} for the preferred
|
|
139
138
|
C{B{start1}.Equidistant}.
|
|
140
139
|
@kwarg tol: Tolerance for convergence and for skew line distance and length
|
|
141
140
|
(C{meter}, conventionally).
|
|
142
|
-
@kwarg
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
ignored if C{B{LatLon} is None}.
|
|
141
|
+
@kwarg LatLon_and_kwds: Optional class C{B{LatLon}=}L{LatLon} to return the
|
|
142
|
+
intersection points and optionally, additional B{C{LatLon}}
|
|
143
|
+
keyword arguments, ignored if C{B{LatLon}=None}.
|
|
146
144
|
|
|
147
145
|
@return: An L{Intersection3Tuple}C{(point, outside1, outside2)} with C{point}
|
|
148
146
|
a B{C{LatLon}} or if C{B{LatLon} is None}, a L{LatLon4Tuple}C{(lat,
|
|
@@ -154,23 +152,23 @@ def intersection3(start1, end1, start2, end2, height=None, wrap=False, # was=Tr
|
|
|
154
152
|
@raise TypeError: Invalid or non-ellipsoidal B{C{start1}}, B{C{end1}},
|
|
155
153
|
B{C{start2}} or B{C{end2}} or invalid B{C{equidistant}}.
|
|
156
154
|
|
|
157
|
-
@note: For each line specified with an initial bearing, a pseudo-end point
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
earth perimeter).
|
|
155
|
+
@note: For each line specified with an initial bearing, a pseudo-end point is
|
|
156
|
+
computed as the C{destination} along that bearing at about 1.5 times the
|
|
157
|
+
distance from the start point to an initial gu-/estimate of the intersection
|
|
158
|
+
point (and between 1/8 and 3/8 of the authalic earth perimeter).
|
|
162
159
|
|
|
163
160
|
@see: U{The B{ellipsoidal} case<https://GIS.StackExchange.com/questions/48937/
|
|
164
161
|
calculating-intersection-of-two-circles>} and U{Karney's paper
|
|
165
162
|
<https://ArXiv.org/pdf/1102.1215.pdf>}, pp 20-21, section B{14. MARITIME
|
|
166
163
|
BOUNDARIES} for more details about the iteration algorithm.
|
|
167
164
|
'''
|
|
165
|
+
kwds = _xkwds(LatLon_and_kwds, LatLon=LatLon)
|
|
168
166
|
return _intersection3(start1, end1, start2, end2, height=height, wrap=wrap,
|
|
169
|
-
|
|
167
|
+
equidistant=equidistant, tol=tol, **kwds)
|
|
170
168
|
|
|
171
169
|
|
|
172
170
|
def intersections2(center1, radius1, center2, radius2, height=None, wrap=False, # was=True
|
|
173
|
-
|
|
171
|
+
equidistant=None, tol=_TOL_M, **LatLon_and_kwds):
|
|
174
172
|
'''I{Iteratively} compute the intersection points of two circles, each defined
|
|
175
173
|
by an (ellipsoidal) center point and a radius.
|
|
176
174
|
|
|
@@ -189,10 +187,9 @@ def intersections2(center1, radius1, center2, radius2, height=None, wrap=False,
|
|
|
189
187
|
the preferred C{B{center1}.Equidistant}.
|
|
190
188
|
@kwarg tol: Convergence tolerance (C{meter}, same units as B{C{radius1}}
|
|
191
189
|
and B{C{radius2}}).
|
|
192
|
-
@kwarg
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
ignored if C{B{LatLon} is None}.
|
|
190
|
+
@kwarg LatLon_and_kwds: Optional class C{B{LatLon}=}L{LatLon} to return the
|
|
191
|
+
intersection points and optionally, additional B{C{LatLon}}
|
|
192
|
+
keyword arguments, ignored if C{B{LatLon}=None}.
|
|
196
193
|
|
|
197
194
|
@return: 2-Tuple of the intersection points, each a B{C{LatLon}} instance
|
|
198
195
|
or L{LatLon4Tuple}C{(lat, lon, height, datum)} if C{B{LatLon} is
|
|
@@ -214,8 +211,9 @@ def intersections2(center1, radius1, center2, radius2, height=None, wrap=False,
|
|
|
214
211
|
U{sphere-sphere<https://MathWorld.Wolfram.com/Sphere-SphereIntersection.html>}
|
|
215
212
|
intersections.
|
|
216
213
|
'''
|
|
214
|
+
kwds = _xkwds(LatLon_and_kwds, LatLon=LatLon)
|
|
217
215
|
return _intersections2(center1, radius1, center2, radius2, height=height, wrap=wrap,
|
|
218
|
-
|
|
216
|
+
equidistant=equidistant, tol=tol, **kwds)
|
|
219
217
|
|
|
220
218
|
|
|
221
219
|
def isclockwise(points, datum=_WGS84, wrap=True):
|
|
@@ -245,9 +243,9 @@ def isclockwise(points, datum=_WGS84, wrap=True):
|
|
|
245
243
|
|
|
246
244
|
|
|
247
245
|
def nearestOn(point, point1, point2, within=True, height=None, wrap=False,
|
|
248
|
-
|
|
249
|
-
'''I{Iteratively} locate the closest point on the geodesic
|
|
250
|
-
two other (
|
|
246
|
+
equidistant=None, tol=_TOL_M, **LatLon_and_kwds):
|
|
247
|
+
'''I{Iteratively} locate the closest point on the geodesic (line)
|
|
248
|
+
between two other (ellipsoidal) points.
|
|
251
249
|
|
|
252
250
|
@arg point: Reference point (C{LatLon}).
|
|
253
251
|
@arg point1: Start point of the geodesic (C{LatLon}).
|
|
@@ -265,16 +263,15 @@ def nearestOn(point, point1, point2, within=True, height=None, wrap=False,
|
|
|
265
263
|
or function L{pygeodesy.equidistant}) or C{None}
|
|
266
264
|
for the preferred C{B{point}.Equidistant}.
|
|
267
265
|
@kwarg tol: Convergence tolerance (C{meter}).
|
|
268
|
-
@kwarg
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
arguments, ignored if C{B{LatLon} is None}.
|
|
266
|
+
@kwarg LatLon_and_kwds: Optional class C{B{LatLon}=}L{LatLon} to return the
|
|
267
|
+
closest point and optionally, additional B{C{LatLon}} keyword
|
|
268
|
+
arguments, ignored if C{B{LatLon}=None}.
|
|
272
269
|
|
|
273
|
-
@return: Closest point, a B{C{LatLon}} instance or if C{B{LatLon}
|
|
274
|
-
|
|
270
|
+
@return: Closest point, a B{C{LatLon}} instance or if C{B{LatLon} is None},
|
|
271
|
+
a L{LatLon4Tuple}C{(lat, lon, height, datum)}.
|
|
275
272
|
|
|
276
|
-
@raise TypeError: Invalid or non-ellipsoidal B{C{point}}, B{C{point1}}
|
|
277
|
-
|
|
273
|
+
@raise TypeError: Invalid or non-ellipsoidal B{C{point}}, B{C{point1}} or
|
|
274
|
+
B{C{point2}} or invalid B{C{equidistant}}.
|
|
278
275
|
|
|
279
276
|
@raise ValueError: No convergence for the B{C{tol}}.
|
|
280
277
|
|
|
@@ -283,8 +280,9 @@ def nearestOn(point, point1, point2, within=True, height=None, wrap=False,
|
|
|
283
280
|
<https://ArXiv.org/pdf/1102.1215.pdf>}, pp 20-21, section B{14. MARITIME
|
|
284
281
|
BOUNDARIES} for more details about the iteration algorithm.
|
|
285
282
|
'''
|
|
283
|
+
kwds = _xkwds(LatLon_and_kwds, LatLon=LatLon)
|
|
286
284
|
return _nearestOn(point, point1, point2, within=within, height=height, wrap=wrap,
|
|
287
|
-
|
|
285
|
+
equidistant=equidistant, tol=tol, **kwds)
|
|
288
286
|
|
|
289
287
|
|
|
290
288
|
def perimeterOf(points, closed=False, datum=_WGS84, wrap=True):
|
pygeodesy/ellipsoids.py
CHANGED
|
@@ -811,9 +811,9 @@ class Ellipsoid(_NamedEnumItem):
|
|
|
811
811
|
|
|
812
812
|
eccentricity = e # eccentricity
|
|
813
813
|
# eccentricity2 = e2 # eccentricity squared
|
|
814
|
-
eccentricity1st2 = e2 # first eccentricity squared
|
|
815
|
-
eccentricity2nd2 = e22 # second eccentricity squared
|
|
816
|
-
eccentricity3rd2 = e32 # third eccentricity squared
|
|
814
|
+
eccentricity1st2 = e2 # first eccentricity squared, signed
|
|
815
|
+
eccentricity2nd2 = e22 # second eccentricity squared, signed
|
|
816
|
+
eccentricity3rd2 = e32 # third eccentricity squared, signed
|
|
817
817
|
|
|
818
818
|
def ecef(self, Ecef=None):
|
|
819
819
|
'''Return U{ECEF<https://WikiPedia.org/wiki/ECEF>} converter.
|
pygeodesy/errors.py
CHANGED
|
@@ -27,7 +27,7 @@ from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, _PYTHON_X_DEV
|
|
|
27
27
|
from copy import copy as _copy
|
|
28
28
|
|
|
29
29
|
__all__ = _ALL_LAZY.errors # _ALL_DOCS('_InvalidError', '_IsnotError') _under
|
|
30
|
-
__version__ = '24.
|
|
30
|
+
__version__ = '24.11.02'
|
|
31
31
|
|
|
32
32
|
_argument_ = 'argument'
|
|
33
33
|
_box_ = 'box'
|
|
@@ -777,7 +777,7 @@ except AttributeError:
|
|
|
777
777
|
return d
|
|
778
778
|
|
|
779
779
|
|
|
780
|
-
# def _xkwds_bool(inst, **kwds): #
|
|
780
|
+
# def _xkwds_bool(inst, **kwds): # unused
|
|
781
781
|
# '''(INTERNAL) Set applicable C{bool} properties/attributes.
|
|
782
782
|
# '''
|
|
783
783
|
# for n, v in kwds.items():
|
|
@@ -789,18 +789,18 @@ except AttributeError:
|
|
|
789
789
|
# setattr(inst, NN(_UNDER_, n), v)
|
|
790
790
|
|
|
791
791
|
|
|
792
|
-
def _xkwds_from(orig, *args, **kwds):
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
792
|
+
# def _xkwds_from(orig, *args, **kwds): # unused
|
|
793
|
+
# '''(INTERNAL) Return the items from C{orig} with the keys
|
|
794
|
+
# from C{kwds} and a value not in C{args} and C{kwds}.
|
|
795
|
+
# '''
|
|
796
|
+
# def _items(orig, args, items):
|
|
797
|
+
# for n, m in items:
|
|
798
|
+
# if n in orig: # n in (orig.keys() & kwds.keys())
|
|
799
|
+
# t = orig[n]
|
|
800
|
+
# if t is not m and t not in args:
|
|
801
|
+
# yield n, t
|
|
802
|
+
#
|
|
803
|
+
# return _items(orig, args, kwds.items())
|
|
804
804
|
|
|
805
805
|
|
|
806
806
|
def _xkwds_get(kwds, **name_default):
|
|
@@ -813,14 +813,14 @@ def _xkwds_get(kwds, **name_default):
|
|
|
813
813
|
raise _xAssertionError(_xkwds_get, kwds, **name_default)
|
|
814
814
|
|
|
815
815
|
|
|
816
|
-
def _xkwds_get_(kwds, **names_defaults):
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
816
|
+
# def _xkwds_get_(kwds, **names_defaults): # unused
|
|
817
|
+
# '''(INTERNAL) Yield each C{kwds} value or its C{default}
|
|
818
|
+
# in I{case-insensitive, alphabetical} C{name} order.
|
|
819
|
+
# '''
|
|
820
|
+
# if not isinstance(kwds, dict):
|
|
821
|
+
# raise _xAssertionError(_xkwds_get_, kwds)
|
|
822
|
+
# for n, v in _MODS.basics.itemsorted(names_defaults):
|
|
823
|
+
# yield kwds.get(n, v)
|
|
824
824
|
|
|
825
825
|
|
|
826
826
|
def _xkwds_get1(kwds, **name_default):
|
|
@@ -844,11 +844,11 @@ def _xkwds_item2(kwds):
|
|
|
844
844
|
raise _xAssertionError(_xkwds_item2, kwds)
|
|
845
845
|
|
|
846
846
|
|
|
847
|
-
def _xkwds_kwds(kwds, **names_defaults):
|
|
847
|
+
def _xkwds_kwds(kwds, **names_defaults): # in .geodesici # PYCHOK no cover
|
|
848
848
|
'''(INTERNAL) Return a C{dict} of C{named_defaults} items replaced with C{kwds}.
|
|
849
849
|
'''
|
|
850
850
|
if not isinstance(kwds, dict):
|
|
851
|
-
raise _xAssertionError(
|
|
851
|
+
raise _xAssertionError(_xkwds_kwds, kwds)
|
|
852
852
|
_g = kwds.get
|
|
853
853
|
return dict((n, _g(n, v)) for n, v in names_defaults.items())
|
|
854
854
|
|
pygeodesy/etm.py
CHANGED
|
@@ -11,7 +11,7 @@ Class L{ExactTransverseMercator} provides C{Exact Transverse Mercator} projectio
|
|
|
11
11
|
instances of class L{Etm} represent ETM C{(easting, northing)} locations. See also
|
|
12
12
|
I{Karney}'s utility U{TransverseMercatorProj<https://GeographicLib.SourceForge.io/C++/doc/
|
|
13
13
|
TransverseMercatorProj.1.html>} and use C{"python[3] -m pygeodesy.etm ..."} to compare
|
|
14
|
-
the results.
|
|
14
|
+
the results, see usage C{"python[3] -m pygeodesy.etm -h"}.
|
|
15
15
|
|
|
16
16
|
Following is a copy of I{Karney}'s U{TransverseMercatorExact.hpp
|
|
17
17
|
<https://GeographicLib.SourceForge.io/C++/doc/TransverseMercatorExact_8hpp_source.html>}
|
|
@@ -65,23 +65,25 @@ from __future__ import division as _; del _ # PYCHOK semicolon
|
|
|
65
65
|
|
|
66
66
|
from pygeodesy.basics import map1, neg, neg_, _xinstanceof
|
|
67
67
|
from pygeodesy.constants import EPS, EPS02, PI_2, PI_4, _K0_UTM, \
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
_1_EPS, _0_0, _0_1, _0_5, _1_0, _2_0, \
|
|
69
|
+
_3_0, _90_0, isnear0, isnear90
|
|
70
|
+
from pygeodesy.constants import _4_0 # PYCHOK used!
|
|
70
71
|
from pygeodesy.datums import _ellipsoidal_datum, _WGS84, _EWGS84
|
|
71
72
|
# from pygeodesy.ellipsoids import _EWGS84 # from .datums
|
|
72
|
-
from pygeodesy.elliptic import
|
|
73
|
+
# from pygeodesy.elliptic import Elliptic # _MODS
|
|
73
74
|
# from pygeodesy.errors import _incompatible # from .named
|
|
74
75
|
# from pygeodesy.fsums import Fsum # from .fmath
|
|
75
76
|
from pygeodesy.fmath import cbrt, hypot, hypot1, hypot2, Fsum
|
|
76
77
|
from pygeodesy.interns import _COMMASPACE_, _near_, _SPACE_, _spherical_
|
|
77
78
|
from pygeodesy.karney import _K_2_4, _copyBit, _diff182, _fix90, \
|
|
78
79
|
_norm2, _norm180, _tand, _unsigned2
|
|
79
|
-
# from pygeodesy.lazily import _ALL_LAZY # from .
|
|
80
|
-
from pygeodesy.named import callername, _incompatible, _NamedBase
|
|
80
|
+
# from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS # from .named
|
|
81
|
+
from pygeodesy.named import callername, _incompatible, _NamedBase, \
|
|
82
|
+
_ALL_LAZY, _MODS
|
|
81
83
|
from pygeodesy.namedTuples import Forward4Tuple, Reverse4Tuple
|
|
82
84
|
from pygeodesy.props import deprecated_method, deprecated_property_RO, \
|
|
83
85
|
Property_RO, property_RO, _update_all, \
|
|
84
|
-
property_doc_
|
|
86
|
+
property_doc_, _allPropertiesOf_n
|
|
85
87
|
from pygeodesy.streprs import Fmt, pairs, unstr
|
|
86
88
|
from pygeodesy.units import Degrees, Scalar_
|
|
87
89
|
from pygeodesy.utily import atan1d, atan2d, _loneg, sincos2
|
|
@@ -91,12 +93,12 @@ from pygeodesy.utm import _cmlon, _LLEB, _parseUTM5, _toBand, _toXtm8, \
|
|
|
91
93
|
from math import asinh, atan2, degrees, radians, sinh, sqrt
|
|
92
94
|
|
|
93
95
|
__all__ = _ALL_LAZY.etm
|
|
94
|
-
__version__ = '24.
|
|
96
|
+
__version__ = '24.11.04'
|
|
95
97
|
|
|
96
|
-
_OVERFLOW = _1_EPS**2 #
|
|
97
|
-
_TAYTOL = pow(EPS,
|
|
98
|
+
_OVERFLOW = _1_EPS**2 # ~2e+31
|
|
99
|
+
_TAYTOL = pow(EPS, 0.6)
|
|
98
100
|
_TAYTOL2 = _TAYTOL * _2_0
|
|
99
|
-
_TOL_10 = EPS
|
|
101
|
+
_TOL_10 = EPS * _0_1
|
|
100
102
|
_TRIPS = 21 # C++ 10
|
|
101
103
|
|
|
102
104
|
|
|
@@ -218,12 +220,12 @@ class Etm(Utm):
|
|
|
218
220
|
class ExactTransverseMercator(_NamedBase):
|
|
219
221
|
'''Pure Python version of Karney's C++ class U{TransverseMercatorExact
|
|
220
222
|
<https://GeographicLib.SourceForge.io/C++/doc/TransverseMercatorExact_8cpp_source.html>},
|
|
221
|
-
a numerically exact transverse Mercator projection,
|
|
223
|
+
a numerically exact transverse Mercator projection, abbreviated as C{TMExact}.
|
|
222
224
|
'''
|
|
223
225
|
_datum = _WGS84 # Datum
|
|
224
226
|
_E = _EWGS84 # Ellipsoid
|
|
225
227
|
_extendp = False # use extended domain
|
|
226
|
-
# _iteration = None # ._sigmaInv2 and ._zetaInv2
|
|
228
|
+
# _iteration = None # _NameBase, ._sigmaInv2 and ._zetaInv2
|
|
227
229
|
_k0 = _K0_UTM # central scale factor
|
|
228
230
|
_lat0 = _0_0 # central parallel
|
|
229
231
|
_lon0 = _0_0 # central meridian
|
|
@@ -240,7 +242,7 @@ class ExactTransverseMercator(_NamedBase):
|
|
|
240
242
|
L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}).
|
|
241
243
|
@kwarg lon0: Central meridian, default (C{degrees180}).
|
|
242
244
|
@kwarg k0: Central scale factor (C{float}).
|
|
243
|
-
@kwarg extendp:
|
|
245
|
+
@kwarg extendp: If C{True}, use the I{extended} domain, I{standard} otherwise (C{bool}).
|
|
244
246
|
@kwarg raiser: If C{True}, throw an L{ETMError} for convergence failures (C{bool}).
|
|
245
247
|
@kwarg name: Optional C{B{name}=NN} for the projection (C{str}).
|
|
246
248
|
|
|
@@ -349,7 +351,7 @@ class ExactTransverseMercator(_NamedBase):
|
|
|
349
351
|
def _Eu(self):
|
|
350
352
|
'''(INTERNAL) Get and cache C{Elliptic(_mu)}.
|
|
351
353
|
'''
|
|
352
|
-
return Elliptic(self._mu)
|
|
354
|
+
return _MODS.elliptic.Elliptic(self._mu)
|
|
353
355
|
|
|
354
356
|
@Property_RO
|
|
355
357
|
def _Eu_cE(self):
|
|
@@ -390,7 +392,7 @@ class ExactTransverseMercator(_NamedBase):
|
|
|
390
392
|
def _Ev(self):
|
|
391
393
|
'''(INTERNAL) Get and cache C{Elliptic(_mv)}.
|
|
392
394
|
'''
|
|
393
|
-
return Elliptic(self._mv)
|
|
395
|
+
return _MODS.elliptic.Elliptic(self._mv)
|
|
394
396
|
|
|
395
397
|
@Property_RO
|
|
396
398
|
def _Ev_cK(self):
|
|
@@ -416,7 +418,7 @@ class ExactTransverseMercator(_NamedBase):
|
|
|
416
418
|
'''
|
|
417
419
|
return self._Ev_cKE * 1.25 # _1_25
|
|
418
420
|
|
|
419
|
-
@
|
|
421
|
+
@property_RO
|
|
420
422
|
def extendp(self):
|
|
421
423
|
'''Get the domain (C{bool}), I{extended} or I{standard}.
|
|
422
424
|
'''
|
|
@@ -635,7 +637,7 @@ class ExactTransverseMercator(_NamedBase):
|
|
|
635
637
|
'''Set the central parallel and meridian.
|
|
636
638
|
|
|
637
639
|
@arg lat0: Latitude of the central parallel (C{degrees90}).
|
|
638
|
-
@arg lon0: Longitude of the central
|
|
640
|
+
@arg lon0: Longitude of the central meridian (C{degrees180}).
|
|
639
641
|
|
|
640
642
|
@return: 2-Tuple C{(lat0, lon0)} of the previous central
|
|
641
643
|
parallel and meridian.
|
|
@@ -644,7 +646,7 @@ class ExactTransverseMercator(_NamedBase):
|
|
|
644
646
|
'''
|
|
645
647
|
t = self._lat0, self.lon0
|
|
646
648
|
self._lat0 = _fix90(Degrees(lat0=lat0, Error=ETMError))
|
|
647
|
-
self. lon0 = lon0
|
|
649
|
+
self. lon0 = lon0 # lon0.setter
|
|
648
650
|
return t
|
|
649
651
|
|
|
650
652
|
def _resets(self, datum):
|
|
@@ -655,15 +657,15 @@ class ExactTransverseMercator(_NamedBase):
|
|
|
655
657
|
@raise ETMError: Near-spherical B{C{datum}} or C{ellipsoid}.
|
|
656
658
|
'''
|
|
657
659
|
E = datum.ellipsoid
|
|
658
|
-
mu = E.e2 # .eccentricity1st2
|
|
660
|
+
mu = E.e2 # E.eccentricity1st2
|
|
659
661
|
mv = E.e21 # _1_0 - mu
|
|
660
662
|
if isnear0(E.e) or isnear0(mu, eps0=EPS02) \
|
|
661
663
|
or isnear0(mv, eps0=EPS02): # or sqrt(mu) != E.e
|
|
662
664
|
raise ETMError(ellipsoid=E, txt=_near_(_spherical_))
|
|
663
665
|
|
|
664
666
|
if self._datum or self._E:
|
|
665
|
-
_i = ExactTransverseMercator.iteration._uname
|
|
666
|
-
_update_all(self, _i, '_sigmaC', '_zetaC') # _under
|
|
667
|
+
_i = ExactTransverseMercator.iteration._uname # property_RO
|
|
668
|
+
_update_all(self, _i, '_sigmaC', '_zetaC', Base=Property_RO) # _under
|
|
667
669
|
|
|
668
670
|
self._E = E
|
|
669
671
|
self._mu = mu
|
|
@@ -757,7 +759,7 @@ class ExactTransverseMercator(_NamedBase):
|
|
|
757
759
|
# k = sqrt(mv + mu / sec2) * sqrt(sec2) * sqrt(q2)
|
|
758
760
|
# = sqrt(mv * sec2 + mu) * sqrt(q2)
|
|
759
761
|
# = sqrt(mv + mv * tau**2 + mu) * sqrt(q2)
|
|
760
|
-
k, q2 = _0_0, (
|
|
762
|
+
k, q2 = _0_0, (snv**2 * mv + cnudnv**2)
|
|
761
763
|
if q2 > 0:
|
|
762
764
|
k2 = (tau**2 + _1_0) * mv + mu
|
|
763
765
|
if k2 > 0:
|
|
@@ -809,9 +811,9 @@ class ExactTransverseMercator(_NamedBase):
|
|
|
809
811
|
d = (cnv**2 + snuv**2 * mu)**2 * self._mv
|
|
810
812
|
r = cnv * dnu * dnv
|
|
811
813
|
i = cnu * snuv * mu
|
|
812
|
-
du = (r
|
|
813
|
-
dv =
|
|
814
|
-
return du, dv
|
|
814
|
+
du = (r + i) * (r - i) / d # (r**2 - i**2) / d
|
|
815
|
+
dv = r * i * _2_0 / d
|
|
816
|
+
return du, neg(dv)
|
|
815
817
|
|
|
816
818
|
def _sigmaInv2(self, xi, eta):
|
|
817
819
|
'''(INTERNAL) Invert C{sigma} using Newton's method.
|
|
@@ -874,7 +876,8 @@ class ExactTransverseMercator(_NamedBase):
|
|
|
874
876
|
'''
|
|
875
877
|
# snu, cnu, dnu = self._Eu.sncndn(u)
|
|
876
878
|
# snv, cnv, dnv = self._Ev.sncndn(v)
|
|
877
|
-
return self._Eu.sncndn(u, **jam) +
|
|
879
|
+
return self._Eu.sncndn(u, **jam) + \
|
|
880
|
+
self._Ev.sncndn(v, **jam)
|
|
878
881
|
|
|
879
882
|
def toStr(self, joined=_COMMASPACE_, **kwds): # PYCHOK signature
|
|
880
883
|
'''Return a C{str} representation.
|
|
@@ -900,21 +903,19 @@ class ExactTransverseMercator(_NamedBase):
|
|
|
900
903
|
real &taup, real &lam)}
|
|
901
904
|
'''
|
|
902
905
|
e, cnu2, mv = self._e, cnu**2, self._mv
|
|
903
|
-
# Overflow value like atan(overflow) = pi/2
|
|
904
|
-
t1 = t2 = _overflow(snu)
|
|
905
906
|
# Lee 54.17 but write
|
|
906
907
|
# atanh(snu * dnv) = asinh(snu * dnv / sqrt(cnu^2 + _mv * snu^2 * snv^2))
|
|
907
908
|
# atanh(_e * snu / dnv) = asinh(_e * snu / sqrt(_mu * cnu^2 + _mv * cnv^2))
|
|
908
|
-
d1 = cnu2 +
|
|
909
|
+
d1 = cnu2 + (snu * snv)**2 * mv
|
|
909
910
|
if d1 > EPS02: # _EPSmin
|
|
910
911
|
t1 = snu * dnv / sqrt(d1)
|
|
911
|
-
else:
|
|
912
|
-
d1 = 0
|
|
913
|
-
d2 = self._mu
|
|
912
|
+
else: # like atan(overflow) = pi/2
|
|
913
|
+
t1, d1 = _overflow(snu), 0
|
|
914
|
+
d2 = cnu2 * self._mu + cnv**2 * mv
|
|
914
915
|
if d2 > EPS02: # _EPSmin
|
|
915
916
|
t2 = sinh(e * asinh(e * snu / sqrt(d2)))
|
|
916
917
|
else:
|
|
917
|
-
d2 = 0
|
|
918
|
+
t2, d2 = _overflow(snu), 0
|
|
918
919
|
# psi = asinh(t1) - asinh(t2)
|
|
919
920
|
# taup = sinh(psi)
|
|
920
921
|
taup = t1 * hypot1(t2) - t2 * hypot1(t1)
|
|
@@ -939,9 +940,9 @@ class ExactTransverseMercator(_NamedBase):
|
|
|
939
940
|
snuv2 = snuv**2 * self._mu
|
|
940
941
|
# Lee 54.21 but write (see A+S 16.21.4)
|
|
941
942
|
# (1 - dnu^2 * snv^2) = (cnv^2 + _mu * snu^2 * snv^2)
|
|
942
|
-
d =
|
|
943
|
-
du =
|
|
944
|
-
dv =
|
|
943
|
+
d = (cnv2 + snuv2)**2 * self._mv # max(d, EPS02)?
|
|
944
|
+
du = (cnv2 - snuv2) * cnu * dnuv / d
|
|
945
|
+
dv = (cnu2 + dnuv2) * cnv * snuv / d
|
|
945
946
|
return du, neg(dv)
|
|
946
947
|
|
|
947
948
|
def _zetaInv2(self, taup, lam):
|
|
@@ -1021,6 +1022,9 @@ class ExactTransverseMercator(_NamedBase):
|
|
|
1021
1022
|
g_k += atan1d(tau), degrees(lam)
|
|
1022
1023
|
return g_k # or (g, k, lat, lon)
|
|
1023
1024
|
|
|
1025
|
+
_allPropertiesOf_n(22, ExactTransverseMercator, Property_RO) # PYCHOK assert
|
|
1026
|
+
del _0_1, _allPropertiesOf_n, EPS, _1_EPS, _EWGS84
|
|
1027
|
+
|
|
1024
1028
|
|
|
1025
1029
|
def _overflow(x):
|
|
1026
1030
|
'''(INTERNAL) Like C{copysign0(OVERFLOW, B{x})}.
|
|
@@ -1102,32 +1106,42 @@ if __name__ == '__main__': # MCCABE 16
|
|
|
1102
1106
|
def _main():
|
|
1103
1107
|
|
|
1104
1108
|
from pygeodesy import fstr, KTransverseMercator
|
|
1105
|
-
|
|
1106
|
-
from pygeodesy.internals import printf, _usage
|
|
1109
|
+
from pygeodesy.interns import _BAR_, _COLONSPACE_, _DASH_, NN
|
|
1110
|
+
from pygeodesy.internals import printf, _usage
|
|
1107
1111
|
from sys import argv, exit as _exit
|
|
1108
1112
|
|
|
1109
|
-
def
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1113
|
+
def _error(why, _a=NN):
|
|
1114
|
+
if _a:
|
|
1115
|
+
why = 'option %r %s' % (_a, why)
|
|
1116
|
+
_exit(_COLONSPACE_(_usage(*argv), why))
|
|
1117
|
+
|
|
1118
|
+
def _help(*why):
|
|
1119
|
+
if why:
|
|
1120
|
+
printf(_COLONSPACE_(_usage(*argv), *why))
|
|
1121
|
+
_exit(_usage(argv[0], '[-s[eries]', _BAR_, '-t]',
|
|
1122
|
+
'[-p[recision] <ndigits>]',
|
|
1123
|
+
'[-f[orward] <lat> <lon>', _BAR_,
|
|
1124
|
+
'-r[everse] <easting> <northing>', _BAR_,
|
|
1125
|
+
'<lat> <lon>]', _BAR_,
|
|
1126
|
+
'-h[elp]'))
|
|
1127
|
+
|
|
1128
|
+
def _result(t4):
|
|
1129
|
+
printf(_COLONSPACE_(tm.classname, fstr(t4, prec=_p, sep=_SPACE_)))
|
|
1116
1130
|
|
|
1117
1131
|
# mimick some of I{Karney}'s utility C{TransverseMercatorProj}
|
|
1118
1132
|
_f = _r = _s = _t = False
|
|
1119
1133
|
_p = -6
|
|
1120
|
-
|
|
1121
|
-
while
|
|
1122
|
-
_a =
|
|
1134
|
+
args = argv[1:]
|
|
1135
|
+
while args and args[0].startswith(_DASH_):
|
|
1136
|
+
_a = args.pop(0)
|
|
1123
1137
|
if len(_a) < 2:
|
|
1124
|
-
|
|
1138
|
+
_error('invalid', _a)
|
|
1125
1139
|
elif '-forward'.startswith(_a):
|
|
1126
1140
|
_f, _r = True, False
|
|
1127
1141
|
elif '-reverse'.startswith(_a):
|
|
1128
1142
|
_f, _r = False, True
|
|
1129
|
-
elif '-precision'.startswith(_a):
|
|
1130
|
-
_p = int(
|
|
1143
|
+
elif '-precision'.startswith(_a) and args:
|
|
1144
|
+
_p = int(args.pop(0))
|
|
1131
1145
|
elif '-series'.startswith(_a):
|
|
1132
1146
|
_s, _t = True, False
|
|
1133
1147
|
elif _a == '-t':
|
|
@@ -1135,52 +1149,46 @@ if __name__ == '__main__': # MCCABE 16
|
|
|
1135
1149
|
elif '-help'.startswith(_a):
|
|
1136
1150
|
_help()
|
|
1137
1151
|
else:
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
f2 = map1(float, *_as[:2])
|
|
1142
|
-
else:
|
|
1143
|
-
printf('%s ...: incomplete', _usage(*argv))
|
|
1144
|
-
_help()
|
|
1145
|
-
|
|
1146
|
-
if _s: # -series
|
|
1147
|
-
tm = KTransverseMercator()
|
|
1148
|
-
else:
|
|
1149
|
-
tm = ExactTransverseMercator(extendp=_t)
|
|
1152
|
+
_error('not supported', _a)
|
|
1153
|
+
if len(args) < 2:
|
|
1154
|
+
_help('incomplete')
|
|
1150
1155
|
|
|
1156
|
+
f2 = map1(float, *args[:2])
|
|
1157
|
+
tm = KTransverseMercator() if _s else \
|
|
1158
|
+
ExactTransverseMercator(extendp=_t)
|
|
1151
1159
|
if _f:
|
|
1152
1160
|
t = tm.forward(*f2)
|
|
1153
1161
|
elif _r:
|
|
1154
1162
|
t = tm.reverse(*f2)
|
|
1155
1163
|
else:
|
|
1156
1164
|
t = tm.forward(*f2)
|
|
1157
|
-
|
|
1165
|
+
_result(t)
|
|
1158
1166
|
t = tm.reverse(t.easting, t.northing)
|
|
1159
|
-
|
|
1167
|
+
_result(t)
|
|
1160
1168
|
|
|
1161
1169
|
_main()
|
|
1162
1170
|
|
|
1163
|
-
# % python3.13 -m pygeodesy.etm -p 12
|
|
1171
|
+
# % python3.13 -m pygeodesy.etm -p 12 33.33 44.44
|
|
1164
1172
|
# ExactTransverseMercator: 4276926.114803905599 4727193.767015309073 28.375536563148 1.233325101778
|
|
1165
1173
|
# ExactTransverseMercator: 33.33 44.44 28.375536563148 1.233325101778
|
|
1166
1174
|
|
|
1167
|
-
# % python3.13 -m pygeodesy.etm -s
|
|
1175
|
+
# % python3.13 -m pygeodesy.etm -s -p 12 33.33 44.44
|
|
1168
1176
|
# KTransverseMercator: 4276926.114803904667 4727193.767015310004 28.375536563148 1.233325101778
|
|
1169
1177
|
# KTransverseMercator: 33.33 44.44 28.375536563148 1.233325101778
|
|
1170
1178
|
|
|
1171
|
-
# % python3.12 -m pygeodesy.etm -p 12
|
|
1179
|
+
# % python3.12 -m pygeodesy.etm -p 12 33.33 44.44
|
|
1172
1180
|
# ExactTransverseMercator: 4276926.11480390653 4727193.767015309073 28.375536563148 1.233325101778
|
|
1173
1181
|
# ExactTransverseMercator: 33.33 44.44 28.375536563148 1.233325101778
|
|
1174
1182
|
|
|
1175
|
-
# % python3.12 -m pygeodesy.etm -s
|
|
1183
|
+
# % python3.12 -m pygeodesy.etm -s -p 12 33.33 44.44
|
|
1176
1184
|
# KTransverseMercator: 4276926.114803904667 4727193.767015310004 28.375536563148 1.233325101778
|
|
1177
1185
|
# KTransverseMercator: 33.33 44.44 28.375536563148 1.233325101778
|
|
1178
1186
|
|
|
1179
|
-
# % python2 -m pygeodesy.etm -p 12
|
|
1187
|
+
# % python2 -m pygeodesy.etm -p 12 33.33 44.44
|
|
1180
1188
|
# ExactTransverseMercator: 4276926.11480390653 4727193.767015309073 28.375536563148 1.233325101778
|
|
1181
1189
|
# ExactTransverseMercator: 33.33 44.44 28.375536563148 1.233325101778
|
|
1182
1190
|
|
|
1183
|
-
# % python2 -m pygeodesy.etm -s
|
|
1191
|
+
# % python2 -m pygeodesy.etm -s -p 12 33.33 44.44
|
|
1184
1192
|
# KTransverseMercator: 4276926.114803904667 4727193.767015310004 28.375536563148 1.233325101778
|
|
1185
1193
|
# KTransverseMercator: 33.33 44.44 28.375536563148 1.233325101778
|
|
1186
1194
|
|