pygeodesy 24.9.29__py2.py3-none-any.whl → 24.10.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.9.29.dist-info → PyGeodesy-24.10.24.dist-info}/METADATA +15 -15
- {PyGeodesy-24.9.29.dist-info → PyGeodesy-24.10.24.dist-info}/RECORD +56 -56
- pygeodesy/__init__.py +20 -19
- pygeodesy/__main__.py +5 -5
- pygeodesy/albers.py +12 -17
- pygeodesy/basics.py +38 -41
- pygeodesy/booleans.py +54 -46
- pygeodesy/cartesianBase.py +2 -2
- pygeodesy/constants.py +20 -16
- pygeodesy/datums.py +3 -3
- pygeodesy/dms.py +250 -270
- pygeodesy/ellipsoidalBase.py +2 -2
- pygeodesy/ellipsoidalBaseDI.py +10 -10
- pygeodesy/ellipsoidalNvector.py +4 -4
- pygeodesy/ellipsoidalVincenty.py +2 -2
- pygeodesy/ellipsoids.py +7 -48
- pygeodesy/elliptic.py +14 -14
- pygeodesy/errors.py +15 -10
- pygeodesy/etm.py +18 -2
- pygeodesy/fmath.py +188 -176
- pygeodesy/formy.py +4 -4
- pygeodesy/fstats.py +54 -56
- pygeodesy/fsums.py +304 -266
- pygeodesy/geodesici.py +43 -40
- pygeodesy/geodesicw.py +3 -3
- pygeodesy/geodesicx/gxarea.py +3 -2
- pygeodesy/geodsolve.py +73 -24
- pygeodesy/geohash.py +2 -2
- pygeodesy/geoids.py +28 -27
- pygeodesy/internals.py +156 -85
- pygeodesy/interns.py +23 -20
- pygeodesy/karney.py +61 -12
- pygeodesy/latlonBase.py +13 -15
- pygeodesy/lazily.py +206 -214
- pygeodesy/mgrs.py +13 -13
- pygeodesy/named.py +11 -10
- pygeodesy/nvectorBase.py +1 -1
- pygeodesy/points.py +2 -2
- pygeodesy/props.py +34 -13
- pygeodesy/rhumb/bases.py +5 -5
- pygeodesy/rhumb/solve.py +7 -8
- pygeodesy/solveBase.py +7 -25
- pygeodesy/sphericalBase.py +20 -23
- pygeodesy/sphericalNvector.py +24 -23
- pygeodesy/sphericalTrigonometry.py +9 -8
- pygeodesy/streprs.py +11 -8
- pygeodesy/trf.py +6 -4
- pygeodesy/triaxials.py +46 -9
- pygeodesy/units.py +4 -3
- pygeodesy/ups.py +6 -6
- pygeodesy/utily.py +2 -2
- pygeodesy/utm.py +2 -2
- pygeodesy/vector3d.py +5 -5
- pygeodesy/vector3dBase.py +4 -5
- {PyGeodesy-24.9.29.dist-info → PyGeodesy-24.10.24.dist-info}/WHEEL +0 -0
- {PyGeodesy-24.9.29.dist-info → PyGeodesy-24.10.24.dist-info}/top_level.txt +0 -0
pygeodesy/dms.py
CHANGED
|
@@ -10,7 +10,7 @@ compass point suffix, including parsing of C{decimal} and C{sexagecimal} degrees
|
|
|
10
10
|
Set env variable C{PYGEODESY_FMT_FORM} to any C{F_...} form to override default C{F_DMS}
|
|
11
11
|
formatting of lat- and longitudes or to an empty string to restore the default.
|
|
12
12
|
|
|
13
|
-
After I{(C) Chris Veness 2011-
|
|
13
|
+
After I{(C) Chris Veness 2011-2024} published under the same MIT Licence**, see
|
|
14
14
|
U{Latitude/Longitude<https://www.Movable-Type.co.UK/scripts/latlong.html>} and
|
|
15
15
|
U{Vector-based geodesy<https://www.Movable-Type.co.UK/scripts/latlong-vectors.html>}.
|
|
16
16
|
|
|
@@ -61,17 +61,18 @@ U{Vector-based geodesy<https://www.Movable-Type.co.UK/scripts/latlong-vectors.ht
|
|
|
61
61
|
non-ascii characters and if so, I{not} C{unicode}.
|
|
62
62
|
'''
|
|
63
63
|
|
|
64
|
-
from pygeodesy.basics import copysign0, isLatLon, isodd, issequence, isstr,
|
|
64
|
+
from pygeodesy.basics import copysign0, isLatLon, isodd, issequence, isstr, \
|
|
65
65
|
neg as _neg # in .ups
|
|
66
66
|
from pygeodesy.constants import _umod_360, _0_0, _0_5, _60_0, _360_0, _3600_0
|
|
67
|
-
from pygeodesy.errors import ParseError, RangeError, _TypeError, _ValueError
|
|
68
|
-
_parseX, rangerrors, _xkwds,
|
|
69
|
-
from pygeodesy.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
67
|
+
from pygeodesy.errors import ParseError, RangeError, _TypeError, _ValueError, \
|
|
68
|
+
_parseX, rangerrors, _xError, _xkwds, _getPYGEODESY
|
|
69
|
+
# from pygeodesy.internals import _getPYGEODESY # from .errors
|
|
70
|
+
from pygeodesy.interns import NN, _COMMA_, _d_, _DASH_, _deg_, _degrees_, _DOT_, \
|
|
71
|
+
_0_, _e_, _E_, _EW_, _f_, _F_, _g_, _MINUS_, _N_, \
|
|
72
|
+
_NE_, _NS_, _NSEW_, _NW_, _PERCENTDOTSTAR_, _PLUS_, \
|
|
73
|
+
_PLUSMINUS_, _QUOTE1_, _QUOTE2_, _radians_, _S_, \
|
|
74
|
+
_SE_, _SPACE_, _SW_, _W_
|
|
75
|
+
from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS
|
|
75
76
|
# from pygeodesy.namedTuples import LatLon2Tuple # _MODS
|
|
76
77
|
# from pygeodesy.props import _throwarning # _MODS
|
|
77
78
|
from pygeodesy.streprs import Fmt, fstr, fstrzs, _0wpF
|
|
@@ -85,12 +86,10 @@ except ImportError: # Python 3+
|
|
|
85
86
|
from string import ascii_letters as _LETTERS
|
|
86
87
|
|
|
87
88
|
__all__ = _ALL_LAZY.dms
|
|
88
|
-
__version__ = '24.
|
|
89
|
+
__version__ = '24.10.18'
|
|
89
90
|
|
|
90
91
|
_beyond_ = 'beyond'
|
|
91
|
-
_DDDMMSS_ = 'DDDMMSS'
|
|
92
92
|
_deg_min_ = 'deg+min'
|
|
93
|
-
_SDIGITS_ = '-0123456789+'
|
|
94
93
|
_sexagecimal_ = 'sexagecimal'
|
|
95
94
|
_SEXAGECIMUL = 1.e4 # sexagecimal C{D.MMSSss} into decimal C{DMMSS.ss}
|
|
96
95
|
|
|
@@ -101,7 +100,7 @@ F_D_, F_DM_, F_DMS_, F_DEG_, F_MIN_, F_SEC_, F_D60_, F__E_, F__F_, F__G
|
|
|
101
100
|
F_D__, F_DM__, F_DMS__, F_DEG__, F_MIN__, F_SEC__, F_D60__, F__E__, F__F__, F__G__, F_RAD__ = (NN(
|
|
102
101
|
_PLUS_, _) for _ in _F_s)
|
|
103
102
|
del _F_s
|
|
104
|
-
_F_DMS =
|
|
103
|
+
_F_DMS = _getPYGEODESY('FMT_FORM') or F_DMS
|
|
105
104
|
|
|
106
105
|
_F_case = {F_D: F_D, F_DEG: F_D, _degrees_: F_D, # unsigned _F_s
|
|
107
106
|
F_DM: F_DM, F_MIN: F_DM, _deg_min_: F_DM,
|
|
@@ -150,13 +149,20 @@ def _DMS3(form, s_D=S_DEG, s_M=S_MIN, s_S=S_SEC, s_DMS=S_DMS, **unused):
|
|
|
150
149
|
def _dms3(d, ddd, p, w):
|
|
151
150
|
'''(INTERNAL) Format C{d} as (deg, min, sec) C{str}s with leading zeros.
|
|
152
151
|
'''
|
|
153
|
-
d
|
|
152
|
+
d = round(d * _3600_0, p)
|
|
153
|
+
d, s = divmod(d, _3600_0)
|
|
154
154
|
m, s = divmod(s, _60_0)
|
|
155
155
|
return (_0wpF(ddd, 0, d),
|
|
156
156
|
_0wpF( 2, 0, m),
|
|
157
157
|
_0wpF(w+2, p, s))
|
|
158
158
|
|
|
159
159
|
|
|
160
|
+
def _DR2(s_D=S_NUL, s_R=S_RAD, **unused):
|
|
161
|
+
'''(INTERNAL) Get the overridden or default C{D} and C{RAD} symbols.
|
|
162
|
+
'''
|
|
163
|
+
return s_D, s_R
|
|
164
|
+
|
|
165
|
+
|
|
160
166
|
def _fstrzs(t, **unused):
|
|
161
167
|
'''(INTERNAL) Pass-thru version of C{.streprs.fstrzs}.
|
|
162
168
|
'''
|
|
@@ -169,79 +175,78 @@ def _split3(strDMS, suffix=_NSEW_):
|
|
|
169
175
|
t = strDMS.strip()
|
|
170
176
|
s = t[:1] # sign or digit
|
|
171
177
|
P = t[-1:] # compass point or digit or dot
|
|
172
|
-
t = t.lstrip(_PLUSMINUS_).rstrip(suffix)
|
|
173
|
-
return s, t, P
|
|
178
|
+
t = t.lstrip(_PLUSMINUS_).rstrip(suffix)
|
|
179
|
+
return s, t.strip(), P
|
|
174
180
|
|
|
175
181
|
|
|
176
|
-
def _toDMS(deg, form, prec, sep, ddd,
|
|
182
|
+
def _toDMS(deg, form, prec, sep, ddd, P, s_D_M_S): # MCCABE 15 in .units
|
|
177
183
|
'''(INTERNAL) Convert C{deg} to C{str}, with/-out sign, DMS symbols and/or suffix.
|
|
178
184
|
'''
|
|
179
|
-
f = form
|
|
180
185
|
try:
|
|
186
|
+
F = None
|
|
181
187
|
deg = float(deg)
|
|
182
|
-
except (TypeError, ValueError) as x:
|
|
183
|
-
raise _ValueError(deg=deg, form=f, prec=prec, cause=x)
|
|
184
|
-
|
|
185
|
-
if f[:1] in _PLUSMINUS_: # signed
|
|
186
|
-
sign = _MINUS_ if deg < 0 else (
|
|
187
|
-
_PLUS_ if deg > 0 and f[:1] == _PLUS_ else NN)
|
|
188
|
-
f = f.lstrip(_PLUSMINUS_)
|
|
189
|
-
suff = NN # no suffix if signed
|
|
190
|
-
else: # suffixed
|
|
191
|
-
sign = NN # no sign if suffixed
|
|
192
|
-
if suff and sep: # no sep if no suffix
|
|
193
|
-
suff = NN(sep, suff)
|
|
194
|
-
try:
|
|
195
|
-
F = _F_case[f] # .strip()
|
|
196
|
-
except KeyError:
|
|
197
|
-
f = f.lower() # .strip()
|
|
198
|
-
F = _F_case.get(f, F_DMS)
|
|
199
188
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
189
|
+
f, s = form, form[:1]
|
|
190
|
+
if s in _PLUSMINUS_: # signed
|
|
191
|
+
n = _MINUS_ if deg < 0 else (
|
|
192
|
+
_PLUS_ if deg > 0 and s == _PLUS_ else NN)
|
|
193
|
+
P = NN # no suffix
|
|
194
|
+
f = f.lstrip(_PLUSMINUS_)
|
|
195
|
+
else: # suffixed
|
|
196
|
+
n = NN
|
|
197
|
+
if sep and P: # no sep if no suffix
|
|
198
|
+
P = NN(sep, P)
|
|
199
|
+
try:
|
|
200
|
+
F = _F_case[f] # .strip()
|
|
201
|
+
except KeyError:
|
|
202
|
+
f = f.lower() # .strip()
|
|
203
|
+
F = _F_case.get(f, F_DMS)
|
|
204
|
+
|
|
205
|
+
if prec is None:
|
|
206
|
+
z = p = _F_prec.get(F, 6)
|
|
207
|
+
else:
|
|
208
|
+
z = int(prec)
|
|
209
|
+
p = abs(z)
|
|
210
|
+
w = p + (1 if p else 0)
|
|
211
|
+
z = fstrzs if z > 1 else _fstrzs
|
|
212
|
+
d = fabs(deg)
|
|
208
213
|
|
|
209
|
-
try:
|
|
210
214
|
if F is F_DMS: # 'deg+min+sec', default
|
|
211
215
|
D, M, S = _DMS3(f, **s_D_M_S)
|
|
212
216
|
d, m, s = _dms3(d, ddd, p, w)
|
|
213
|
-
t = NN(
|
|
214
|
-
|
|
215
|
-
|
|
217
|
+
t = NN(n, d, D, sep,
|
|
218
|
+
m, M, sep,
|
|
219
|
+
z(s), S, P)
|
|
216
220
|
|
|
217
221
|
elif F is F_DM: # 'deg+min'
|
|
218
222
|
D, M, _ = _DMS3(f, **s_D_M_S)
|
|
219
223
|
d, m = divmod(round(d * _60_0, p), _60_0)
|
|
220
|
-
t = NN(
|
|
221
|
-
|
|
224
|
+
t = NN(n, _0wpF(ddd, 0, d), D, sep,
|
|
225
|
+
z(_0wpF(w+2, p, m)), M, P)
|
|
222
226
|
|
|
223
227
|
elif F is F_D: # 'deg'
|
|
224
228
|
D, _, _ = _DMS3(f, **s_D_M_S)
|
|
225
|
-
t = NN(
|
|
229
|
+
t = NN(n, z(_0wpF(w+ddd, p, d)), D, P)
|
|
226
230
|
|
|
227
231
|
elif F is F_D60: # 'deg.MM|SSss|'
|
|
228
232
|
D, M, S = _D603(sep, **s_D_M_S)
|
|
229
233
|
d, m, s = _dms3(d, ddd, p, w)
|
|
230
|
-
t = z(s).split(_DOT_) + [S,
|
|
231
|
-
t = NN(
|
|
234
|
+
t = z(s).split(_DOT_) + [S, P]
|
|
235
|
+
t = NN(n, d, D, m, M, *t)
|
|
232
236
|
|
|
233
237
|
elif F is F_RAD:
|
|
234
|
-
R =
|
|
235
|
-
r =
|
|
236
|
-
t =
|
|
238
|
+
_, R = _DR2(**s_D_M_S)
|
|
239
|
+
r = NN(_PERCENTDOTSTAR_, _F_) % (p, radians(d))
|
|
240
|
+
t = NN(n, z(r), R, P)
|
|
237
241
|
|
|
238
242
|
else: # F in (F__E, F__F, F__G)
|
|
239
|
-
D =
|
|
240
|
-
d =
|
|
241
|
-
t =
|
|
243
|
+
D, _ = _DR2(**s_D_M_S)
|
|
244
|
+
d = NN(_PERCENTDOTSTAR_, F) % (p, d) # XXX f?
|
|
245
|
+
t = NN(n, z(d, ap1z=F is F__G), D, P)
|
|
242
246
|
|
|
243
247
|
except Exception as x:
|
|
244
|
-
|
|
248
|
+
d = {} if F is None else dict(F_=F, P=P)
|
|
249
|
+
raise _xError(x, deg=deg, form=form, prec=prec, **d)
|
|
245
250
|
|
|
246
251
|
return t # NOT unicode in Python 2-
|
|
247
252
|
|
|
@@ -250,26 +255,15 @@ def bearingDMS(bearing, form=F_D, prec=None, sep=S_SEP, **s_D_M_S):
|
|
|
250
255
|
'''Convert bearing to a string (without compass point suffix).
|
|
251
256
|
|
|
252
257
|
@arg bearing: Bearing from North (compass C{degrees360}).
|
|
253
|
-
@kwarg form: Format specifier for B{C{deg}} (C{str} or
|
|
254
|
-
L{F_DM}, L{F_DMS}, L{F_DEG}, L{F_MIN}, L{F_SEC},
|
|
255
|
-
L{F_D60}, L{F__E}, L{F__F}, L{F__G}, L{F_RAD},
|
|
256
|
-
L{F_D_}, L{F_DM_}, L{F_DMS_}, L{F_DEG_}, L{F_MIN_},
|
|
257
|
-
L{F_SEC_}, L{F_D60_}, L{F__E_}, L{F__F_}, L{F__G_},
|
|
258
|
-
L{F_RAD_}, L{F_D__}, L{F_DM__}, L{F_DMS__}, L{F_DEG__},
|
|
259
|
-
L{F_MIN__}, L{F_SEC__}, L{F_D60__}, L{F__E__},
|
|
260
|
-
L{F__F__}, L{F__G__} or L{F_RAD__}).
|
|
258
|
+
@kwarg form: Format specifier for B{C{deg}} (C{str} or C{F_...}).
|
|
261
259
|
@kwarg prec: Number of decimal digits (0..9 or C{None} for default).
|
|
262
|
-
Trailing zero decimals are stripped for B{C{prec}}
|
|
263
|
-
values of 1 and above, but kept for negative B{C{prec}}.
|
|
264
260
|
@kwarg sep: Separator between degrees, minutes, seconds, suffix (C{str}).
|
|
265
|
-
@kwarg s_D_M_S: Optional keyword arguments
|
|
266
|
-
|
|
267
|
-
cancel all DMS symbols, defaults L{S_DEG}, L{S_MIN}
|
|
268
|
-
respectively L{S_SEC}.
|
|
261
|
+
@kwarg s_D_M_S: Optional keyword arguments to override any or cancel all
|
|
262
|
+
DMS symbol suffixes, see function L{pygeodesy.toDMS}.
|
|
269
263
|
|
|
270
264
|
@return: Compass degrees per the specified B{C{form}} (C{str}).
|
|
271
265
|
|
|
272
|
-
@see: Function L{pygeodesy.toDMS}.
|
|
266
|
+
@see: Function L{pygeodesy.toDMS} and its B{Notes} for further details.
|
|
273
267
|
'''
|
|
274
268
|
return _toDMS(_umod_360(bearing), form, prec, sep, 1, NN, s_D_M_S)
|
|
275
269
|
|
|
@@ -317,26 +311,15 @@ def compassDMS(bearing, form=F_D, prec=None, sep=S_SEP, **s_D_M_S):
|
|
|
317
311
|
'''Convert bearing to a string suffixed with compass point.
|
|
318
312
|
|
|
319
313
|
@arg bearing: Bearing from North (compass C{degrees360}).
|
|
320
|
-
@kwarg form: Format specifier for B{C{deg}} (C{str} or
|
|
321
|
-
L{F_DM}, L{F_DMS}, L{F_DEG}, L{F_MIN}, L{F_SEC},
|
|
322
|
-
L{F_D60}, L{F__E}, L{F__F}, L{F__G}, L{F_RAD},
|
|
323
|
-
L{F_D_}, L{F_DM_}, L{F_DMS_}, L{F_DEG_}, L{F_MIN_},
|
|
324
|
-
L{F_SEC_}, L{F_D60_}, L{F__E_}, L{F__F_}, L{F__G_},
|
|
325
|
-
L{F_RAD_}, L{F_D__}, L{F_DM__}, L{F_DMS__}, L{F_DEG__},
|
|
326
|
-
L{F_MIN__}, L{F_SEC__}, L{F_D60__}, L{F__E__},
|
|
327
|
-
L{F__F__}, L{F__G__} or L{F_RAD__}).
|
|
314
|
+
@kwarg form: Format specifier for B{C{deg}} (C{str} or C{F_...}).
|
|
328
315
|
@kwarg prec: Number of decimal digits (0..9 or C{None} for default).
|
|
329
|
-
Trailing zero decimals are stripped for B{C{prec}}
|
|
330
|
-
values of 1 and above, but kept for negative B{C{prec}}.
|
|
331
316
|
@kwarg sep: Separator between degrees, minutes, seconds, suffix (C{str}).
|
|
332
|
-
@kwarg s_D_M_S: Optional keyword arguments
|
|
333
|
-
|
|
334
|
-
cancel all DMS symbols, defaults L{S_DEG}, L{S_MIN}
|
|
335
|
-
respectively L{S_SEC}.
|
|
317
|
+
@kwarg s_D_M_S: Optional keyword arguments to override any or cancel all
|
|
318
|
+
DMS symbol suffixes, see function L{pygeodesy.toDMS}.
|
|
336
319
|
|
|
337
320
|
@return: Compass degrees and point in the specified form (C{str}).
|
|
338
321
|
|
|
339
|
-
@see: Function L{pygeodesy.toDMS}.
|
|
322
|
+
@see: Function L{pygeodesy.toDMS} and its B{Notes} for further details.
|
|
340
323
|
'''
|
|
341
324
|
b = _umod_360(bearing)
|
|
342
325
|
return _toDMS(b, form, prec, sep, 1, compassPoint(b), s_D_M_S)
|
|
@@ -371,7 +354,7 @@ def compassPoint(bearing, prec=3):
|
|
|
371
354
|
w *= int(b * m / _360_0 + _0_5) % m
|
|
372
355
|
return _WINDS[w]
|
|
373
356
|
except Exception as x:
|
|
374
|
-
raise
|
|
357
|
+
raise _xError(x, bearing=bearing, prec=prec)
|
|
375
358
|
|
|
376
359
|
|
|
377
360
|
def degDMS(deg, prec=6, s_D=S_DEG, s_M=S_MIN, s_S=S_SEC, neg=_MINUS_, pos=NN):
|
|
@@ -379,13 +362,13 @@ def degDMS(deg, prec=6, s_D=S_DEG, s_M=S_MIN, s_S=S_SEC, neg=_MINUS_, pos=NN):
|
|
|
379
362
|
|
|
380
363
|
@arg deg: Value in degrees (C{scalar degrees}).
|
|
381
364
|
@kwarg prec: Number of decimal digits (0..9 or C{None} for default).
|
|
382
|
-
Trailing zero decimals are stripped for B{
|
|
383
|
-
|
|
365
|
+
Trailing zero decimals are stripped for C{B{prec}=1}
|
|
366
|
+
and above, but kept for negative B{C{prec}}.
|
|
384
367
|
@kwarg s_D: D symbol for degrees (C{str}).
|
|
385
|
-
@kwarg s_M: M symbol for minutes (C{str}) or C{""}.
|
|
386
|
-
@kwarg s_S: S symbol for seconds (C{str}) or C{""}.
|
|
368
|
+
@kwarg s_M: M symbol for minutes (C{str}) or C{""} aka L{pygeodesy.NN}.
|
|
369
|
+
@kwarg s_S: S symbol for seconds (C{str}) or C{""} aka L{pygeodesy.NN}.
|
|
387
370
|
@kwarg neg: Optional sign for negative (C{'-'}).
|
|
388
|
-
@kwarg pos: Optional sign for positive (C{
|
|
371
|
+
@kwarg pos: Optional sign for positive (C{""}) aka L{pygeodesy.NN}.
|
|
389
372
|
|
|
390
373
|
@return: I{Either} degrees, minutes I{or} seconds (C{str}).
|
|
391
374
|
|
|
@@ -393,8 +376,8 @@ def degDMS(deg, prec=6, s_D=S_DEG, s_M=S_MIN, s_S=S_SEC, neg=_MINUS_, pos=NN):
|
|
|
393
376
|
'''
|
|
394
377
|
try:
|
|
395
378
|
deg = float(deg)
|
|
396
|
-
except
|
|
397
|
-
raise
|
|
379
|
+
except Exception as x:
|
|
380
|
+
raise _xError(x, deg=deg, prec=prec)
|
|
398
381
|
|
|
399
382
|
d, s = fabs(deg), s_D
|
|
400
383
|
if d < 1:
|
|
@@ -421,40 +404,28 @@ def latDMS(deg, form=_F_DMS, prec=None, sep=S_SEP, **s_D_M_S):
|
|
|
421
404
|
'''Convert latitude to a string, optionally suffixed with N or S.
|
|
422
405
|
|
|
423
406
|
@arg deg: Latitude to be formatted (C{scalar degrees}).
|
|
424
|
-
@kwarg form: Format specifier for B{C{deg}} (C{str} or
|
|
425
|
-
L{F_DM}, L{F_DMS}, L{F_DEG}, L{F_MIN}, L{F_SEC},
|
|
426
|
-
L{F_D60}, L{F__E}, L{F__F}, L{F__G}, L{F_RAD},
|
|
427
|
-
L{F_D_}, L{F_DM_}, L{F_DMS_}, L{F_DEG_}, L{F_MIN_},
|
|
428
|
-
L{F_SEC_}, L{F_D60_}, L{F__E_}, L{F__F_}, L{F__G_},
|
|
429
|
-
L{F_RAD_}, L{F_D__}, L{F_DM__}, L{F_DMS__}, L{F_DEG__},
|
|
430
|
-
L{F_MIN__}, L{F_SEC__}, L{F_D60__}, L{F__E__},
|
|
431
|
-
L{F__F__}, L{F__G__} or L{F_RAD__}).
|
|
407
|
+
@kwarg form: Format specifier for B{C{deg}} (C{str} or C{F_...}).
|
|
432
408
|
@kwarg prec: Number of decimal digits (0..9 or C{None} for default).
|
|
433
|
-
Trailing zero decimals are stripped for B{C{prec}}
|
|
434
|
-
values of 1 and above, but kept for negative B{C{prec}}.
|
|
435
409
|
@kwarg sep: Separator between degrees, minutes, seconds, suffix (C{str}).
|
|
436
|
-
@kwarg s_D_M_S: Optional keyword arguments
|
|
437
|
-
|
|
438
|
-
cancel all DMS symbols, defaults L{S_DEG}, L{S_MIN}
|
|
439
|
-
respectively L{S_SEC}.
|
|
410
|
+
@kwarg s_D_M_S: Optional keyword arguments to override any or cancel all
|
|
411
|
+
DMS symbol suffixes, see function L{pygeodesy.toDMS}.
|
|
440
412
|
|
|
441
413
|
@return: Degrees in the specified form (C{str}).
|
|
442
414
|
|
|
443
|
-
@see:
|
|
415
|
+
@see: Function L{pygeodesy.toDMS} and its B{Notes} for further details.
|
|
444
416
|
'''
|
|
445
|
-
|
|
446
|
-
return _toDMS(deg, form, prec, sep, 2,
|
|
417
|
+
P = _S_ if deg < 0 else _N_
|
|
418
|
+
return _toDMS(deg, form, prec, sep, 2, P, s_D_M_S)
|
|
447
419
|
|
|
448
420
|
|
|
449
421
|
def latlonDMS(lls, **m_form_prec_sep_s_D_M_S):
|
|
450
422
|
'''Convert one or more C{LatLon} instances to strings.
|
|
451
423
|
|
|
452
|
-
@arg lls: Single (C{LatLon}) or
|
|
424
|
+
@arg lls: Single (C{LatLon}) or an iterable (C{LatLon}s).
|
|
453
425
|
@kwarg m_form_prec_sep_s_D_M_S: Optional keyword arguments C{B{m}eter},
|
|
454
426
|
C{B{form}at}, C{B{prec}ision}, B{C{s_D}}, B{C{s_M}}, B{C{s_S}},
|
|
455
|
-
B{C{s_DMS}} and I{DEPRECATED}
|
|
456
|
-
|
|
457
|
-
L{pygeodesy.lonDMS} for more details.
|
|
427
|
+
B{C{s_DMS}} and I{DEPRECATED} C{B{sep}=None}, see function
|
|
428
|
+
L{pygeodesy.toDMS} and method C{LatLon.toStr} for more details.
|
|
458
429
|
|
|
459
430
|
@return: A C{tuple} of C{str}s if B{C{lls}} is a list, sequence, tuple, etc.
|
|
460
431
|
of C{LatLon}s or a single C{str} if B{C{lls}} is a single C{LatLon}.
|
|
@@ -462,15 +433,15 @@ def latlonDMS(lls, **m_form_prec_sep_s_D_M_S):
|
|
|
462
433
|
@see: Functions L{pygeodesy.latlonDMS_}, L{pygeodesy.latDMS}, L{pygeodesy.lonDMS}
|
|
463
434
|
and L{pygeodesy.toDMS} and method C{LatLon.toStr}.
|
|
464
435
|
|
|
465
|
-
@note: Keyword argument C{B{sep}=None} to join
|
|
466
|
-
|
|
436
|
+
@note: Keyword argument C{B{sep}=None} to join the resturned C{tuple} has
|
|
437
|
+
been I{DEPRECATED}, use C{B{sep}.join(B{latlonDMS_}(...))} instead.
|
|
467
438
|
'''
|
|
468
439
|
sep, kwds = _latlonDMS_sep2(latlonDMS, **m_form_prec_sep_s_D_M_S)
|
|
469
440
|
if isLatLon(lls):
|
|
470
441
|
t = lls.toStr(**kwds)
|
|
471
442
|
elif issequence(lls):
|
|
472
443
|
t = tuple(ll.toStr(**kwds) for ll in lls)
|
|
473
|
-
if sep: # XXX
|
|
444
|
+
if sep: # XXX DEPRECATED, to be removed
|
|
474
445
|
t = sep.join(t)
|
|
475
446
|
else:
|
|
476
447
|
raise _TypeError(lls=lls, **m_form_prec_sep_s_D_M_S)
|
|
@@ -481,12 +452,10 @@ def latlonDMS_(*lls, **m_form_prec_sep_s_D_M_S):
|
|
|
481
452
|
'''Convert one or more C{LatLon} instances to strings.
|
|
482
453
|
|
|
483
454
|
@arg lls: The instances (C{LatLon}s), all positional arguments.
|
|
484
|
-
@kwarg m_form_prec_sep_s_D_M_S: Optional keyword arguments
|
|
485
|
-
C{B{
|
|
486
|
-
B{C{
|
|
487
|
-
|
|
488
|
-
functions L{pygeodesy.latDMS} and L{pygeodesy.lonDMS}
|
|
489
|
-
for more details.
|
|
455
|
+
@kwarg m_form_prec_sep_s_D_M_S: Optional keyword arguments C{B{m}eter},
|
|
456
|
+
C{B{form}at}, C{B{prec}ision}, B{C{s_D}}, B{C{s_M}}, B{C{s_S}},
|
|
457
|
+
B{C{s_DMS}} and I{DEPRECATED} C{B{sep}=None}, see function
|
|
458
|
+
L{pygeodesy.toDMS} and method C{LatLon.toStr} for more details.
|
|
490
459
|
|
|
491
460
|
@return: A C{tuple} of C{str}s if 2 or more C{LatLon} instances
|
|
492
461
|
or a single C{str} if only a single C{LatLon} instance
|
|
@@ -496,9 +465,8 @@ def latlonDMS_(*lls, **m_form_prec_sep_s_D_M_S):
|
|
|
496
465
|
L{pygeodesy.lonDMS} and L{pygeodesy.toDMS} and method
|
|
497
466
|
C{LatLon.toStr}.
|
|
498
467
|
|
|
499
|
-
@note: Keyword argument C{B{sep}=None} to join
|
|
500
|
-
|
|
501
|
-
use C{B{sep}.join(B{latlonDMS_}(...))} instead.
|
|
468
|
+
@note: Keyword argument C{B{sep}=None} to join the resturned C{tuple} has
|
|
469
|
+
been I{DEPRECATED}, use C{B{sep}.join(B{latlonDMS_}(...))} instead.
|
|
502
470
|
'''
|
|
503
471
|
sep, kwds = _latlonDMS_sep2(latlonDMS, **m_form_prec_sep_s_D_M_S)
|
|
504
472
|
if not lls:
|
|
@@ -510,9 +478,11 @@ def latlonDMS_(*lls, **m_form_prec_sep_s_D_M_S):
|
|
|
510
478
|
|
|
511
479
|
|
|
512
480
|
def _latlonDMS_sep2(where, sep=None, **kwds):
|
|
513
|
-
'''DEPRECATED,
|
|
514
|
-
if sep:
|
|
515
|
-
|
|
481
|
+
'''DEPRECATED, use %r.join(%s(...)) instead.'''
|
|
482
|
+
if sep: # PYCHOK no cover
|
|
483
|
+
i = _MODS.inters
|
|
484
|
+
k = Fmt.EQUAL(sep=repr(sep))
|
|
485
|
+
k = _SPACE_(i._keyword_, i._arg_, k, i._of_)
|
|
516
486
|
n = where.__name__
|
|
517
487
|
t = _latlonDMS_sep2.__doc__ % (sep, n)
|
|
518
488
|
_MODS.props._throwarning(k, n, t)
|
|
@@ -523,41 +493,29 @@ def lonDMS(deg, form=_F_DMS, prec=None, sep=S_SEP, **s_D_M_S):
|
|
|
523
493
|
'''Convert longitude to a string, optionally suffixed with E or W.
|
|
524
494
|
|
|
525
495
|
@arg deg: Longitude to be formatted (C{scalar degrees}).
|
|
526
|
-
@kwarg form: Format specifier for B{C{deg}} (C{str} or
|
|
527
|
-
L{F_DM}, L{F_DMS}, L{F_DEG}, L{F_MIN}, L{F_SEC},
|
|
528
|
-
L{F_D60}, L{F__E}, L{F__F}, L{F__G}, L{F_RAD},
|
|
529
|
-
L{F_D_}, L{F_DM_}, L{F_DMS_}, L{F_DEG_}, L{F_MIN_},
|
|
530
|
-
L{F_SEC_}, L{F_D60_}, L{F__E_}, L{F__F_}, L{F__G_},
|
|
531
|
-
L{F_RAD_}, L{F_D__}, L{F_DM__}, L{F_DMS__}, L{F_DEG__},
|
|
532
|
-
L{F_MIN__}, L{F_SEC__}, L{F_D60__}, L{F__E__},
|
|
533
|
-
L{F__F__}, L{F__G__} or L{F_RAD__}).
|
|
496
|
+
@kwarg form: Format specifier for B{C{deg}} (C{str} or C{F_...}).
|
|
534
497
|
@kwarg prec: Number of decimal digits (0..9 or C{None} for default).
|
|
535
|
-
Trailing zero decimals are stripped for B{C{prec}}
|
|
536
|
-
values of 1 and above, but kept for negative B{C{prec}}.
|
|
537
498
|
@kwarg sep: Separator between degrees, minutes, seconds, suffix (C{str}).
|
|
538
|
-
@kwarg s_D_M_S: Optional keyword arguments
|
|
539
|
-
|
|
540
|
-
cancel all DMS symbols, defaults L{S_DEG}, L{S_MIN}
|
|
541
|
-
respectively L{S_SEC}.
|
|
499
|
+
@kwarg s_D_M_S: Optional keyword arguments to override any or cancel all
|
|
500
|
+
DMS symbol suffixes.
|
|
542
501
|
|
|
543
502
|
@return: Degrees in the specified form (C{str}).
|
|
544
503
|
|
|
545
|
-
@see:
|
|
504
|
+
@see: Function L{pygeodesy.toDMS} and its B{Notes} for further details.
|
|
546
505
|
'''
|
|
547
|
-
|
|
548
|
-
return _toDMS(deg, form, prec, sep, 3,
|
|
506
|
+
P = _W_ if deg < 0 else _E_
|
|
507
|
+
return _toDMS(deg, form, prec, sep, 3, P, s_D_M_S)
|
|
549
508
|
|
|
550
509
|
|
|
551
510
|
def normDMS(strDMS, norm=None, **s_D_M_S):
|
|
552
|
-
'''Normalize all degrees, minutes and seconds (DMS) I{
|
|
511
|
+
'''Normalize all degrees, minutes and seconds (DMS) I{symbol} suffixes in
|
|
553
512
|
a string to the default symbols L{S_DEG}, L{S_MIN}, L{S_SEC}.
|
|
554
513
|
|
|
555
514
|
@arg strDMS: Original DMS string (C{str}).
|
|
556
|
-
@kwarg norm: Optional replacement symbol (C{str}) or C{None} for
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
C{B{s_M}=str}, C{B{s_S}=str} and/or C{B{s_R}=str}
|
|
515
|
+
@kwarg norm: Optional replacement symbol (C{str}) or C{None} for the default
|
|
516
|
+
DMS symbol). Use C{B{norm}=""} to remove all DMS symbols.
|
|
517
|
+
@kwarg s_D_M_S: Optional, alternate DMS symbol suffixes C{B{s_D}=}L{S_DEG},
|
|
518
|
+
C{B{s_M}=}L{S_MIN}, C{B{s_S}=}L{S_SEC} and C{B{s_R}=}L{S_RAD}
|
|
561
519
|
for radians, each to be replaced by B{C{norm}}.
|
|
562
520
|
|
|
563
521
|
@return: Normalized DMS (C{str}).
|
|
@@ -588,13 +546,14 @@ def normDMS(strDMS, norm=None, **s_D_M_S):
|
|
|
588
546
|
return strDMS # NOT unicode in Python 2-
|
|
589
547
|
|
|
590
548
|
|
|
591
|
-
def parseDDDMMSS(strDDDMMSS, suffix=_NSEW_, sep=S_SEP, clip=0, sexagecimal=False):
|
|
549
|
+
def parseDDDMMSS(strDDDMMSS, suffix=_NSEW_, sep=S_SEP, clip=0, sexagecimal=False):
|
|
592
550
|
'''Parse a lat- or longitude represention forms as [D]DDMMSS in degrees.
|
|
593
551
|
|
|
594
552
|
@arg strDDDMMSS: Degrees in any of several forms (C{str}) and types (C{float},
|
|
595
553
|
C{int}, other).
|
|
596
|
-
@kwarg suffix: Optional
|
|
597
|
-
@kwarg sep: Optional separator between "[D]DD", "MM", "SS", B{C{suffix}}
|
|
554
|
+
@kwarg suffix: Optional compass points (C{str}), valid in B{C{strDDDMMSS}}.
|
|
555
|
+
@kwarg sep: Optional separator between "[D]DD", "MM", "SS", B{C{suffix}}
|
|
556
|
+
(L{S_SEP}) in B{C{strDDDMMSS}}.
|
|
598
557
|
@kwarg clip: Optionally, limit value to range C{-/+B{clip}} (C{degrees}).
|
|
599
558
|
@kwarg sexagecimal: If C{True}, convert C{"D.MMSS"} or C{float(D.MMSS)} to
|
|
600
559
|
C{base-60} "MM" and "SS" digits. See C{form}s L{F_D60},
|
|
@@ -603,8 +562,8 @@ def parseDDDMMSS(strDDDMMSS, suffix=_NSEW_, sep=S_SEP, clip=0, sexagecimal=False
|
|
|
603
562
|
@return: Degrees (C{float}).
|
|
604
563
|
|
|
605
564
|
@raise ParseError: Invalid B{C{strDDDMMSS}} or B{C{clip}} or the form of
|
|
606
|
-
B{C{strDDDMMSS}} is incompatible with
|
|
607
|
-
B{C{suffix}} compass point.
|
|
565
|
+
B{C{strDDDMMSS}} is incompatible with or invalid for the
|
|
566
|
+
given B{C{suffix}} compass point(s).
|
|
608
567
|
|
|
609
568
|
@raise RangeError: Value of B{C{strDDDMMSS}} outside the valid C{-/+B{clip}}
|
|
610
569
|
range and L{rangerrors<pygeodesy.rangerrors>} is C{True}.
|
|
@@ -629,63 +588,39 @@ def parseDDDMMSS(strDDDMMSS, suffix=_NSEW_, sep=S_SEP, clip=0, sexagecimal=False
|
|
|
629
588
|
L{pygeodesy.parse3llh}.
|
|
630
589
|
'''
|
|
631
590
|
def _DDDMMSS(strDDDMMSS, suffix, sep, clip, sexagecimal):
|
|
632
|
-
S = suffix.upper()
|
|
591
|
+
S = suffix.upper() or _N_ # bool('' in _NE_) is True
|
|
633
592
|
if isstr(strDDDMMSS):
|
|
634
|
-
t =
|
|
635
|
-
s, t, P =
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
f = NN.join(f)
|
|
639
|
-
if 1 < n < 8 and f.isdigit() and ( # dddN/S/E/W or ddd or +/-ddd
|
|
640
|
-
(P in S and s.isdigit()) or
|
|
641
|
-
(P.isdigit() and s in _SDIGITS_ # PYCHOK indent
|
|
642
|
-
and S in _WINDS)):
|
|
643
|
-
# check [D]DDMMSS form and compass point
|
|
644
|
-
X = _EW_ if isodd(n) else _NS_
|
|
645
|
-
if not (P in X or (S in X and (P.isdigit() or P == _DOT_))):
|
|
646
|
-
t = _DDDMMSS_[int(X is _NS_):(n | 1)], _DASH_.join(X)
|
|
647
|
-
raise ParseError('form %s applies %s' % t)
|
|
593
|
+
t = strDDDMMSS.replace(sep, NN) if sep else strDDDMMSS
|
|
594
|
+
n, s, t, f, P, X = _DDDMMSS6(t, S)
|
|
595
|
+
if X:
|
|
596
|
+
pass
|
|
648
597
|
elif not sexagecimal: # try other forms
|
|
649
598
|
return _DMS2deg(strDDDMMSS, S, sep, clip, {})
|
|
650
599
|
|
|
651
600
|
if sexagecimal: # move decimal dot from ...
|
|
652
601
|
n += 4 # ... [D]DD.MMSSs to [D]DDMMSS.s
|
|
653
602
|
if n < 6:
|
|
654
|
-
|
|
603
|
+
t = _SPACE_(_sexagecimal_, 'digits')
|
|
604
|
+
raise ParseError(t, n)
|
|
655
605
|
z = n - len(f) # zeros to append
|
|
656
606
|
t = (f + (_0_ * z)) if z > 0 else _DOT_(f[:n], f[n:])
|
|
657
607
|
f = _0_0 # fraction
|
|
658
|
-
|
|
659
608
|
else: # float or int to [D]DDMMSS[.fff]
|
|
660
|
-
f
|
|
661
|
-
|
|
662
|
-
f *= _SEXAGECIMUL
|
|
663
|
-
m = 6
|
|
664
|
-
s = P = _0_ # anything except NN, _S_, _SW_, _W_
|
|
665
|
-
if f < 0:
|
|
666
|
-
f = -f
|
|
667
|
-
s = _MINUS_
|
|
668
|
-
f, i = modf(f) # returns ...
|
|
669
|
-
t = str(int(i)) # ... float(i)
|
|
670
|
-
n = len(t) # number of digits to ...
|
|
671
|
-
if n < m: # ... required min or ...
|
|
672
|
-
t = (_0_ * (m - n)) + t
|
|
673
|
-
# ... match the given compass point
|
|
674
|
-
elif S in (_NS_ if isodd(n) else _EW_):
|
|
675
|
-
t = _0_ + t
|
|
676
|
-
# P = S
|
|
677
|
-
# elif n > 1:
|
|
678
|
-
# P = (_EW_ if isodd(n) else _NS_)[0]
|
|
679
|
-
n = len(t)
|
|
609
|
+
f = float(strDDDMMSS)
|
|
610
|
+
n, s, t, f, P = _DDDMMSS5(f, S, sexagecimal)
|
|
680
611
|
|
|
681
612
|
if n < 4: # [D]DD[.ddd]
|
|
682
|
-
|
|
613
|
+
if t:
|
|
614
|
+
f += float(t)
|
|
615
|
+
t = f,
|
|
683
616
|
else:
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
617
|
+
n -= 2
|
|
618
|
+
f += float(t[n:])
|
|
619
|
+
if n < 4: # [D]DDMM[.mmm]
|
|
620
|
+
t = float(t[:n]), f
|
|
687
621
|
else: # [D]DDMMSS[.sss]
|
|
688
|
-
|
|
622
|
+
d = n - 2
|
|
623
|
+
t = float(t[:d]), float(t[d:n]), f
|
|
689
624
|
d = _dms2deg(s, P, *t)
|
|
690
625
|
return clipDegrees(d, float(clip)) if clip else d
|
|
691
626
|
|
|
@@ -693,13 +628,54 @@ def parseDDDMMSS(strDDDMMSS, suffix=_NSEW_, sep=S_SEP, clip=0, sexagecimal=False
|
|
|
693
628
|
strDDDMMSS=strDDDMMSS, suffix=suffix, sexagecimal=sexagecimal)
|
|
694
629
|
|
|
695
630
|
|
|
696
|
-
def
|
|
697
|
-
'''(INTERNAL)
|
|
631
|
+
def _DDDMMSS5(f, S, sexagecimal):
|
|
632
|
+
'''(INTERNAL) Partial C{parseDDDMMSS} of C{float}.
|
|
633
|
+
'''
|
|
634
|
+
if sexagecimal:
|
|
635
|
+
f *= _SEXAGECIMUL
|
|
636
|
+
m = 6
|
|
637
|
+
else:
|
|
638
|
+
m = 0
|
|
639
|
+
s = P = _PLUS_ # anything except NN, _S_, _SW_, _W_
|
|
640
|
+
if f < 0:
|
|
641
|
+
f = -f
|
|
642
|
+
s = _MINUS_
|
|
643
|
+
f, i = modf(f) # returns ...
|
|
644
|
+
t = str(int(i)) # ... float(i)
|
|
645
|
+
n = len(t) # number of digits to ...
|
|
646
|
+
if n < m: # ... required min or ...
|
|
647
|
+
t = (_0_ * (m - n)) + t
|
|
648
|
+
# ... match the given compass point
|
|
649
|
+
elif S in (_NS_ if isodd(n) else _EW_):
|
|
650
|
+
t = _0_ + t
|
|
651
|
+
# P = S
|
|
652
|
+
# elif n > 1:
|
|
653
|
+
# P = (_EW_ if isodd(n) else _NS_)[0]
|
|
654
|
+
return len(t), s, t, f, P # float(f) fraction
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
def _DDDMMSS6(t, S):
|
|
658
|
+
'''(INTERNAL) Partial C{parseDDDMMSS} of C{str}.
|
|
698
659
|
'''
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
660
|
+
s, t, P = _split3(t, S)
|
|
661
|
+
f = t.split(_DOT_)
|
|
662
|
+
if len(f) > 2:
|
|
663
|
+
raise ParseError('dots', len(f) - 1)
|
|
664
|
+
n = len(f[0])
|
|
665
|
+
f = NN.join(f)
|
|
666
|
+
if 1 < n < 8 and f.isdigit() and ( # dddN/S/E/W or ddd or +/-ddd
|
|
667
|
+
(P in S and s.isdigit()) or
|
|
668
|
+
(P.isdigit() and S in _WINDS # PYCHOK indent
|
|
669
|
+
and (s in _PLUSMINUS_ or s.isdigit()))):
|
|
670
|
+
# check [D]DDMMSS form and compass point
|
|
671
|
+
X = _EW_ if isodd(n) else _NS_
|
|
672
|
+
if not (P in X or (S in X and (P.isdigit() or P == _DOT_))):
|
|
673
|
+
t = parseDDDMMSS.__name__[5 if isodd(n) else 6:]
|
|
674
|
+
t = _SPACE_('form', t, 'applies', _DASH_.join(X))
|
|
675
|
+
raise ParseError(t)
|
|
676
|
+
else:
|
|
677
|
+
X = NN
|
|
678
|
+
return n, s, t, f, P, X # str(f) fraction
|
|
703
679
|
|
|
704
680
|
|
|
705
681
|
def _DMS2deg(strDMS, suffix, sep, clip, s_D_M_S):
|
|
@@ -707,7 +683,6 @@ def _DMS2deg(strDMS, suffix, sep, clip, s_D_M_S):
|
|
|
707
683
|
'''
|
|
708
684
|
try:
|
|
709
685
|
d = float(strDMS)
|
|
710
|
-
|
|
711
686
|
except (TypeError, ValueError):
|
|
712
687
|
s, t, P = _split3(strDMS, suffix.upper())
|
|
713
688
|
if sep: # remove all DMS symbols
|
|
@@ -715,29 +690,40 @@ def _DMS2deg(strDMS, suffix, sep, clip, s_D_M_S):
|
|
|
715
690
|
t = normDMS(t, norm=NN, **s_D_M_S)
|
|
716
691
|
else: # replace all DMS symbols
|
|
717
692
|
t = normDMS(t, norm=_SPACE_, **s_D_M_S)
|
|
718
|
-
t =
|
|
719
|
-
d = _dms2deg(s, P, *t
|
|
720
|
-
|
|
693
|
+
t = t.strip().split()
|
|
694
|
+
d = _dms2deg(s, P, *map(float, t))
|
|
721
695
|
return clipDegrees(d, float(clip)) if clip else d
|
|
722
696
|
|
|
723
697
|
|
|
698
|
+
def _dms2deg(n, P, d, m=_0_0, s=_0_0):
|
|
699
|
+
'''(INTERNAL) Helper for C{parseDDDMMSS}, C{parseRad} and C{_DMS2deg}.
|
|
700
|
+
'''
|
|
701
|
+
m += s / _60_0
|
|
702
|
+
d += m / _60_0
|
|
703
|
+
if n == _MINUS_ or (P and P in _SW_):
|
|
704
|
+
d = _neg(d)
|
|
705
|
+
return d
|
|
706
|
+
|
|
707
|
+
|
|
724
708
|
def parseDMS(strDMS, suffix=_NSEW_, sep=S_SEP, clip=0, **s_D_M_S): # MCCABE 14
|
|
725
709
|
'''Parse a lat- or longitude representation in C{degrees}.
|
|
726
710
|
|
|
727
|
-
This is very flexible on formats, allowing signed decimal
|
|
728
|
-
|
|
729
|
-
|
|
711
|
+
This is very flexible on formats, allowing signed decimal degrees, degrees
|
|
712
|
+
and minutes or degrees minutes and seconds optionally suffixed by a cardinal
|
|
713
|
+
compass point.
|
|
730
714
|
|
|
731
|
-
A variety of symbols, separators and suffixes are accepted,
|
|
732
|
-
|
|
715
|
+
A variety of symbols, separators and suffixes are accepted, for example
|
|
716
|
+
"3°37′09″W". Minutes and seconds may be omitted.
|
|
733
717
|
|
|
734
|
-
@arg strDMS: Degrees in any of several forms (C{str}) and
|
|
735
|
-
|
|
718
|
+
@arg strDMS: Degrees in any of several forms (C{str}) and types (C{float},
|
|
719
|
+
C{int}, other).
|
|
736
720
|
@kwarg suffix: Optional, valid compass points (C{str}, C{tuple}).
|
|
737
|
-
@kwarg sep: Optional separator between deg
|
|
721
|
+
@kwarg sep: Optional separator between C{deg°}, C{min′}, C{sec″},
|
|
722
|
+
B{C{suffix}} (C{''}).
|
|
738
723
|
@kwarg clip: Optionally, limit value to range C{-/+B{clip}} (C{degrees}).
|
|
739
|
-
@kwarg s_D_M_S: Optional, alternate symbol for degrees
|
|
740
|
-
|
|
724
|
+
@kwarg s_D_M_S: Optional, alternate DMS symbol suffixes for degrees
|
|
725
|
+
C{B{s_D}=}L{S_DEG}, minutes C{B{s_M}=}L{S_MIN} and
|
|
726
|
+
seconds C{B{s_S}=}L{S_SEC}, see function L{pygeodesy.toDMS}.
|
|
741
727
|
|
|
742
728
|
@return: Degrees (C{float}).
|
|
743
729
|
|
|
@@ -767,8 +753,9 @@ def parseDMS2(strLat, strLon, sep=S_SEP, clipLat=90, clipLon=180, wrap=False, **
|
|
|
767
753
|
@kwarg clipLon: Limit longitude to range C{-/+B{clipLon}} (C{degrees}).
|
|
768
754
|
@kwarg wrap: If C{True}, wrap or I{normalize} the lat- and longitude,
|
|
769
755
|
overriding B{C{clipLat}} and B{C{clipLon}} (C{bool}).
|
|
770
|
-
@kwarg s_D_M_S: Optional, alternate symbol for degrees
|
|
771
|
-
|
|
756
|
+
@kwarg s_D_M_S: Optional, alternate DMS symbol suffixes for degrees
|
|
757
|
+
C{B{s_D}=}L{S_DEG}, minutes C{B{s_M}=}L{S_MIN} and
|
|
758
|
+
seconds C{B{s_S}=}L{S_SEC}, see function L{pygeodesy.toDMS}.
|
|
772
759
|
|
|
773
760
|
@return: A L{LatLon2Tuple}C{(lat, lon)} in C{degrees}.
|
|
774
761
|
|
|
@@ -791,14 +778,14 @@ def _2Tuple(strLat, strLon, clipLat, clipLon, wrap, **kwds):
|
|
|
791
778
|
'''
|
|
792
779
|
if wrap:
|
|
793
780
|
_W = _MODS.utily._Wrap
|
|
794
|
-
|
|
795
|
-
|
|
781
|
+
ll = _W.latlon(parseDMS(strLat, suffix=_NS_, **kwds),
|
|
782
|
+
parseDMS(strLon, suffix=_EW_, **kwds))
|
|
796
783
|
else:
|
|
797
784
|
# if wrap is None:
|
|
798
785
|
# clipLat = clipLon = 0
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
return _MODS.namedTuples.LatLon2Tuple(
|
|
786
|
+
ll = (parseDMS(strLat, suffix=_NS_, clip=clipLat, **kwds),
|
|
787
|
+
parseDMS(strLon, suffix=_EW_, clip=clipLon, **kwds))
|
|
788
|
+
return _MODS.namedTuples.LatLon2Tuple(*ll)
|
|
802
789
|
|
|
803
790
|
|
|
804
791
|
def parse3llh(strllh, height=0, sep=_COMMA_, clipLat=90, clipLon=180, wrap=False, **s_D_M_S):
|
|
@@ -819,8 +806,9 @@ def parse3llh(strllh, height=0, sep=_COMMA_, clipLat=90, clipLon=180, wrap=False
|
|
|
819
806
|
@kwarg clipLon: Limit longitude to range C{-/+B{clipLon}} (C{degrees}).
|
|
820
807
|
@kwarg wrap: If C{True}, wrap or I{normalize} the lat- and longitude,
|
|
821
808
|
overriding B{C{clipLat}} and B{C{clipLon}} (C{bool}).
|
|
822
|
-
@kwarg s_D_M_S: Optional, alternate symbol for degrees
|
|
823
|
-
|
|
809
|
+
@kwarg s_D_M_S: Optional, alternate DMS symbol suffixes for degrees
|
|
810
|
+
C{B{s_D}=}L{S_DEG}, minutes C{B{s_M}=}L{S_MIN} and
|
|
811
|
+
seconds C{B{s_S}=}L{S_SEC}, see function L{pygeodesy.toDMS}.
|
|
824
812
|
|
|
825
813
|
@return: A L{LatLon3Tuple}C{(lat, lon, height)} in C{degrees},
|
|
826
814
|
C{degrees} and C{float}.
|
|
@@ -836,7 +824,6 @@ def parse3llh(strllh, height=0, sep=_COMMA_, clipLat=90, clipLon=180, wrap=False
|
|
|
836
824
|
@see: Functions L{pygeodesy.parseDDDMMSS}, L{pygeodesy.parseDMS},
|
|
837
825
|
L{pygeodesy.parseDMS2} and L{pygeodesy.toDMS}.
|
|
838
826
|
'''
|
|
839
|
-
|
|
840
827
|
def _3llh(strllh, height, sep, wrap):
|
|
841
828
|
ll = strllh.strip().split(sep)
|
|
842
829
|
if len(ll) > 2: # XXX interpret height unit
|
|
@@ -849,7 +836,8 @@ def parse3llh(strllh, height=0, sep=_COMMA_, clipLat=90, clipLon=180, wrap=False
|
|
|
849
836
|
a, b = [_.strip() for _ in ll] # PYCHOK false
|
|
850
837
|
if a[-1:] in _EW_ or b[-1:] in _NS_:
|
|
851
838
|
a, b = b, a
|
|
852
|
-
|
|
839
|
+
t = _2Tuple(a, b, clipLat, clipLon, wrap, **s_D_M_S)
|
|
840
|
+
return t.to3Tuple(h)
|
|
853
841
|
|
|
854
842
|
return _parseX(_3llh, strllh, height, sep, wrap, strllh=strllh)
|
|
855
843
|
|
|
@@ -871,11 +859,9 @@ def parseRad(strRad, suffix=_NSEW_, clip=0):
|
|
|
871
859
|
def _Rad(strRad, suffix, clip):
|
|
872
860
|
try:
|
|
873
861
|
r = float(strRad)
|
|
874
|
-
|
|
875
862
|
except (TypeError, ValueError):
|
|
876
863
|
s, t, P = _split3(strRad, suffix.upper())
|
|
877
864
|
r = _dms2deg(s, P, float(t))
|
|
878
|
-
|
|
879
865
|
return clipRadians(r, float(clip)) if clip else r
|
|
880
866
|
|
|
881
867
|
return _parseX(_Rad, strRad, suffix, clip, strRad=strRad, suffix=suffix)
|
|
@@ -884,13 +870,11 @@ def parseRad(strRad, suffix=_NSEW_, clip=0):
|
|
|
884
870
|
def precision(form, prec=None):
|
|
885
871
|
'''Set the default precison for a given F_ form.
|
|
886
872
|
|
|
887
|
-
@arg form: L{F_D}, L{F_DM}, L{F_DMS}, L{F_DEG}, L{F_MIN},
|
|
888
|
-
L{
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
for B{C{prec}} values of 1 and above, but kept
|
|
893
|
-
for negative B{C{prec}}.
|
|
873
|
+
@arg form: L{F_D}, L{F_DM}, L{F_DMS}, L{F_DEG}, L{F_MIN}, L{F_SEC},
|
|
874
|
+
L{F_D60}, L{F__E}, L{F__F}, L{F__G} or L{F_RAD} (C{str}).
|
|
875
|
+
@kwarg prec: Number of decimal digits (0..9 or C{None} for default).
|
|
876
|
+
Trailing zero decimals are stripped for C{B{prec}=1}
|
|
877
|
+
and above, but kept for negative B{C{prec}}.
|
|
894
878
|
|
|
895
879
|
@return: Previous precision for the B{C{form}} (C{int}).
|
|
896
880
|
|
|
@@ -901,10 +885,9 @@ def precision(form, prec=None):
|
|
|
901
885
|
p = _F_prec[form]
|
|
902
886
|
except KeyError:
|
|
903
887
|
raise _ValueError(form=form)
|
|
904
|
-
|
|
905
888
|
if prec is not None: # set as default
|
|
906
|
-
|
|
907
|
-
|
|
889
|
+
P_ = _MODS.units.Precision_
|
|
890
|
+
_F_prec[form] = P_(prec, low=-9, high=9)
|
|
908
891
|
return p
|
|
909
892
|
|
|
910
893
|
|
|
@@ -912,41 +895,38 @@ def toDMS(deg, form=_F_DMS, prec=2, sep=S_SEP, ddd=2, neg=_MINUS_, pos=_PLUS_, *
|
|
|
912
895
|
'''Convert I{signed} C{degrees} to string, without suffix.
|
|
913
896
|
|
|
914
897
|
@arg deg: Degrees to be formatted (C{scalar degrees}).
|
|
915
|
-
@kwarg form: Format specifier for B{C{deg}} (C{str} or L{F_D},
|
|
916
|
-
L{
|
|
917
|
-
L{
|
|
918
|
-
L{
|
|
919
|
-
L{
|
|
920
|
-
L{F_RAD_}, L{F_D__}, L{F_DM__}, L{F_DMS__}, L{F_DEG__},
|
|
921
|
-
L{F_MIN__}, L{F_SEC__}, L{F_D60__}, L{F__E__},
|
|
898
|
+
@kwarg form: Format specifier for B{C{deg}} (C{str} or L{F_D}, L{F_DM}, L{F_DMS},
|
|
899
|
+
L{F_DEG}, L{F_MIN}, L{F_SEC}, L{F_D60}, L{F__E}, L{F__F}, L{F__G},
|
|
900
|
+
L{F_RAD}, L{F_D_}, L{F_DM_}, L{F_DMS_}, L{F_DEG_}, L{F_MIN_}, L{F_SEC_},
|
|
901
|
+
L{F_D60_}, L{F__E_}, L{F__F_}, L{F__G_}, L{F_RAD_}, L{F_D__}, L{F_DM__},
|
|
902
|
+
L{F_DMS__}, L{F_DEG__}, L{F_MIN__}, L{F_SEC__}, L{F_D60__}, L{F__E__},
|
|
922
903
|
L{F__F__}, L{F__G__} or L{F_RAD__}).
|
|
923
|
-
@kwarg prec: Number of decimal digits (0..9 or C{None} for default).
|
|
924
|
-
|
|
925
|
-
|
|
904
|
+
@kwarg prec: Number of decimal digits (0..9 or C{None} for default). Trailing zero
|
|
905
|
+
decimals are stripped for C{B{prec}=1} and above, but kept for negative
|
|
906
|
+
B{C{prec}}.
|
|
926
907
|
@kwarg sep: Separator between degrees, minutes, seconds, suffix (C{str}).
|
|
927
|
-
@kwarg ddd: Number of digits for B{C{deg}°} (2 or 3).
|
|
908
|
+
@kwarg ddd: Number of (integer) digits for B{C{deg}°} (2 or 3).
|
|
928
909
|
@kwarg neg: Prefix for negative B{C{deg}} (C{'-'}).
|
|
929
910
|
@kwarg pos: Prefix for positive B{C{deg}} and signed B{C{form}} (C{'+'}).
|
|
930
|
-
@kwarg s_D_M_S: Optional keyword arguments C{B{s_D}=
|
|
931
|
-
C{B{s_S}=
|
|
932
|
-
cancel all DMS
|
|
933
|
-
|
|
911
|
+
@kwarg s_D_M_S: Optional keyword arguments C{B{s_D}=}L{S_DEG}, C{B{s_M}=}L{S_MIN}
|
|
912
|
+
C{B{s_S}=}L{S_SEC}, C{B{s_R}=}L{S_RAD} and C{B{s_DMS}=True} to
|
|
913
|
+
override any or cancel all DMS symbol suffixes. See the B{Notes}
|
|
914
|
+
below.
|
|
934
915
|
|
|
935
916
|
@return: Degrees in the specified form (C{str}).
|
|
936
917
|
|
|
937
|
-
@note: The degrees, minutes and seconds (DMS) symbol can be overridden in
|
|
938
|
-
|
|
939
|
-
C{B{
|
|
940
|
-
|
|
941
|
-
C{B{S_NUL}=NN}.
|
|
918
|
+
@note: The degrees, minutes and seconds (DMS) symbol can be overridden in this and
|
|
919
|
+
other C{*DMS} functions by using optional keyword argments C{B{s_D}="d"},
|
|
920
|
+
C{B{s_M}="'"} respectively C{B{s_S}='"'}. Using B{C{s_DMS}=None} cancels
|
|
921
|
+
all C{DMS} symbols to C{""} aka L{pygeodesy.NN}.
|
|
942
922
|
|
|
943
923
|
@note: Sexagecimal format B{C{F_D60}} supports overridable pseudo-DMS symbols
|
|
944
924
|
positioned at C{"[D]DD<B{s_D}>MM<B{s_M}>SS<B{s_S}>"} with defaults
|
|
945
925
|
C{B{s_D}="."}, C{B{s_M}=B{sep}} and C{B{s_S}=}L{pygeodesy.NN}.
|
|
946
926
|
|
|
947
|
-
@note: Formats B{C{F__E}}, B{C{F__F}} and B{C{F__G}}
|
|
948
|
-
|
|
949
|
-
Likewise for B{C{F_RAD}}
|
|
927
|
+
@note: Formats B{C{F__E}}, B{C{F__F}} and B{C{F__G}} is extended with a C{D}-only
|
|
928
|
+
symbol suffix if defined with keyword argument C{B{s_D}=}L{pygeodesy.NN}.
|
|
929
|
+
Likewise for B{C{F_RAD}} forms with keyword argument C{B{s_R}=}L{S_RAD}.
|
|
950
930
|
|
|
951
931
|
@see: Function L{pygeodesy.degDMS}
|
|
952
932
|
'''
|