pygeodesy 25.4.8__py2.py3-none-any.whl → 25.5.5__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/__init__.py +36 -31
- pygeodesy/__main__.py +3 -3
- pygeodesy/albers.py +29 -36
- pygeodesy/auxilats/_CX_4.py +2 -2
- pygeodesy/auxilats/_CX_6.py +2 -2
- pygeodesy/auxilats/_CX_8.py +2 -2
- pygeodesy/auxilats/_CX_Rs.py +9 -9
- pygeodesy/auxilats/__init__.py +3 -3
- pygeodesy/auxilats/__main__.py +8 -6
- pygeodesy/auxilats/auxAngle.py +2 -2
- pygeodesy/auxilats/auxLat.py +5 -5
- pygeodesy/auxilats/auxily.py +5 -3
- pygeodesy/azimuthal.py +7 -6
- pygeodesy/basics.py +32 -18
- pygeodesy/booleans.py +18 -16
- pygeodesy/cartesianBase.py +26 -24
- pygeodesy/clipy.py +11 -10
- pygeodesy/constants.py +11 -10
- pygeodesy/css.py +14 -11
- pygeodesy/datums.py +8 -8
- pygeodesy/deprecated/bases.py +2 -2
- pygeodesy/deprecated/classes.py +2 -2
- pygeodesy/deprecated/consterns.py +4 -4
- pygeodesy/dms.py +8 -8
- pygeodesy/ecef.py +22 -29
- pygeodesy/ecefLocals.py +186 -0
- pygeodesy/elevations.py +9 -8
- pygeodesy/ellipsoidalBase.py +19 -8
- pygeodesy/ellipsoidalBaseDI.py +17 -15
- pygeodesy/ellipsoidalNvector.py +6 -3
- pygeodesy/ellipsoidalVincenty.py +4 -1
- pygeodesy/ellipsoids.py +186 -164
- pygeodesy/elliptic.py +9 -9
- pygeodesy/errors.py +44 -43
- pygeodesy/etm.py +7 -7
- pygeodesy/fmath.py +30 -14
- pygeodesy/formy.py +11 -12
- pygeodesy/frechet.py +216 -109
- pygeodesy/fstats.py +5 -4
- pygeodesy/fsums.py +79 -78
- pygeodesy/gars.py +4 -3
- pygeodesy/geodesici.py +15 -14
- pygeodesy/geodesicw.py +34 -32
- pygeodesy/geodesicx/__init__.py +1 -1
- pygeodesy/geodesicx/__main__.py +11 -9
- pygeodesy/geodesicx/gx.py +30 -33
- pygeodesy/geodesicx/gxarea.py +2 -2
- pygeodesy/geodesicx/gxline.py +5 -5
- pygeodesy/geodsolve.py +18 -17
- pygeodesy/geohash.py +5 -5
- pygeodesy/geoids.py +34 -31
- pygeodesy/hausdorff.py +17 -13
- pygeodesy/heights.py +2 -4
- pygeodesy/internals.py +28 -44
- pygeodesy/interns.py +10 -7
- pygeodesy/iters.py +8 -8
- pygeodesy/karney.py +68 -62
- pygeodesy/ktm.py +5 -5
- pygeodesy/latlonBase.py +20 -21
- pygeodesy/lazily.py +104 -78
- pygeodesy/lcc.py +11 -9
- pygeodesy/ltp.py +56 -58
- pygeodesy/ltpTuples.py +35 -36
- pygeodesy/mgrs.py +7 -6
- pygeodesy/named.py +48 -177
- pygeodesy/nvectorBase.py +7 -7
- pygeodesy/osgr.py +9 -8
- pygeodesy/points.py +12 -10
- pygeodesy/props.py +25 -25
- pygeodesy/resections.py +83 -80
- pygeodesy/rhumb/__init__.py +1 -1
- pygeodesy/rhumb/aux_.py +7 -7
- pygeodesy/rhumb/bases.py +22 -20
- pygeodesy/rhumb/ekx.py +6 -6
- pygeodesy/rhumb/solve.py +15 -15
- pygeodesy/solveBase.py +3 -3
- pygeodesy/sphericalBase.py +6 -6
- pygeodesy/sphericalNvector.py +6 -5
- pygeodesy/sphericalTrigonometry.py +8 -7
- pygeodesy/streprs.py +14 -14
- pygeodesy/trf.py +14 -12
- pygeodesy/triaxials.py +29 -26
- pygeodesy/units.py +5 -4
- pygeodesy/unitsBase.py +5 -4
- pygeodesy/ups.py +3 -3
- pygeodesy/utily.py +4 -4
- pygeodesy/utmups.py +4 -4
- pygeodesy/utmupsBase.py +110 -18
- pygeodesy/vector2d.py +20 -13
- pygeodesy/vector3d.py +7 -6
- pygeodesy/webmercator.py +6 -5
- pygeodesy/wgrs.py +6 -5
- {pygeodesy-25.4.8.dist-info → pygeodesy-25.5.5.dist-info}/METADATA +30 -25
- pygeodesy-25.5.5.dist-info/RECORD +119 -0
- pygeodesy-25.4.8.dist-info/RECORD +0 -118
- {pygeodesy-25.4.8.dist-info → pygeodesy-25.5.5.dist-info}/WHEEL +0 -0
- {pygeodesy-25.4.8.dist-info → pygeodesy-25.5.5.dist-info}/top_level.txt +0 -0
pygeodesy/rhumb/bases.py
CHANGED
|
@@ -33,9 +33,9 @@ from pygeodesy.errors import IntersectionError, RhumbError, _xdatum, \
|
|
|
33
33
|
from pygeodesy.fmath import euclid, favg, sqrt_a, Fsum
|
|
34
34
|
# from pygeodesy.formy import opposing # _MODS
|
|
35
35
|
# from pygeodesy.fsums import Fsum # from .fmath
|
|
36
|
-
from pygeodesy.internals import
|
|
36
|
+
from pygeodesy.internals import typename, _under
|
|
37
37
|
from pygeodesy.interns import NN, _coincident_, _COMMASPACE_, _Dash, \
|
|
38
|
-
_parallel_, _too_
|
|
38
|
+
_DMAIN_, _parallel_, _too_
|
|
39
39
|
from pygeodesy.karney import _atan2d, Caps, _CapsBase, _diff182, _fix90, \
|
|
40
40
|
_norm180, GDict
|
|
41
41
|
# from pygeodesy.ktm import KTransverseMercator, _AlpCoeffs # _MODS
|
|
@@ -52,7 +52,7 @@ from pygeodesy.vector3d import _intersect3d3, Vector3d # in .Intersection below
|
|
|
52
52
|
from math import cos, fabs
|
|
53
53
|
|
|
54
54
|
__all__ = ()
|
|
55
|
-
__version__ = '
|
|
55
|
+
__version__ = '25.04.14'
|
|
56
56
|
|
|
57
57
|
_anti_ = _Dash('anti')
|
|
58
58
|
_rls = [] # instances of C{RbumbLine...} to be updated
|
|
@@ -231,9 +231,10 @@ class RhumbBase(_CapsBase):
|
|
|
231
231
|
@arg azi12: Azimuth of the rhumb line (compass C{degrees}).
|
|
232
232
|
@kwarg caps_name: Optional keyword arguments C{B{name}=NN} and
|
|
233
233
|
C{B{caps}=Caps.STANDARD}, a bit-or'ed combination of
|
|
234
|
-
L{Caps} values specifying the
|
|
235
|
-
Include C{Caps.LINE_OFF} if
|
|
236
|
-
should I{not
|
|
234
|
+
L{Caps<pygeodesy.karney.Caps>} values specifying the
|
|
235
|
+
required capabilities. Include C{Caps.LINE_OFF} if
|
|
236
|
+
updates to the B{C{rhumb}} should I{not be reflected}
|
|
237
|
+
in this rhumb line.
|
|
237
238
|
|
|
238
239
|
@return: A C{RhumbLine...} instance and invoke its method
|
|
239
240
|
C{.Position} to compute each point.
|
|
@@ -397,9 +398,10 @@ class RhumbBase(_CapsBase):
|
|
|
397
398
|
@arg lon2: Longitude of the second point (C{degrees180}).
|
|
398
399
|
@kwarg caps_name: Optional keyword arguments C{B{name}=NN} and
|
|
399
400
|
C{B{caps}=Caps.STANDARD}, a bit-or'ed combination of
|
|
400
|
-
L{Caps} values specifying the
|
|
401
|
-
Include C{Caps.LINE_OFF} if
|
|
402
|
-
should I{not
|
|
401
|
+
L{Caps<pygeodesy.karney.Caps>} values specifying the
|
|
402
|
+
required capabilities. Include C{Caps.LINE_OFF} if
|
|
403
|
+
updates to the B{C{rhumb}} should I{not be reflected}
|
|
404
|
+
in this rhumb line.
|
|
403
405
|
|
|
404
406
|
@return: A C{RhumbLine...} instance and invoke its method
|
|
405
407
|
C{ArcPosition} or C{Position} to compute points.
|
|
@@ -517,8 +519,8 @@ class RhumbLineBase(_CapsBase):
|
|
|
517
519
|
|
|
518
520
|
@arg a12: The angle along this rhumb line from its origin to the
|
|
519
521
|
point (C{degrees}), can be negative.
|
|
520
|
-
@kwarg outmask: Bit-or'ed combination of L{Caps}
|
|
521
|
-
the quantities to be returned.
|
|
522
|
+
@kwarg outmask: Bit-or'ed combination of L{Caps<pygeodesy.karney.Caps>}
|
|
523
|
+
values specifying the quantities to be returned.
|
|
522
524
|
|
|
523
525
|
@return: L{GDict} with 4 to 8 items C{azi12, a12, s12, S12, lat2,
|
|
524
526
|
lon2, lat1, lon1} with latitude C{lat2} and longitude
|
|
@@ -634,7 +636,7 @@ class RhumbLineBase(_CapsBase):
|
|
|
634
636
|
t = dict(lat3=q.lat2, lon3=q.lon2, azi03=q.azi02, a03=q.a02, s03=a)
|
|
635
637
|
if a < r:
|
|
636
638
|
t.update(iteration=q.iteration, lat0=q.lat1, lon0=q.lon1, # or lat0, lon0
|
|
637
|
-
name=
|
|
639
|
+
name=typename(self.Intersecant2, self.name))
|
|
638
640
|
if fabs(a) < EPS0: # coincident centers
|
|
639
641
|
d, h = _0_0, r
|
|
640
642
|
else:
|
|
@@ -660,7 +662,7 @@ class RhumbLineBase(_CapsBase):
|
|
|
660
662
|
def intersection2(self, other, **tol_eps): # PYCHOK no cover
|
|
661
663
|
'''DEPRECATED on 23.10.10, use method L{Intersection}.'''
|
|
662
664
|
p = self.Intersection(other, **tol_eps)
|
|
663
|
-
r = LatLon2Tuple(p.lat2, p.lon2, name=self.intersection2
|
|
665
|
+
r = LatLon2Tuple(p.lat2, p.lon2, name=typename(self.intersection2))
|
|
664
666
|
r._iteration = p.iteration
|
|
665
667
|
return r
|
|
666
668
|
|
|
@@ -701,7 +703,7 @@ class RhumbLineBase(_CapsBase):
|
|
|
701
703
|
_s_3d, s_az = self._xTM3d, self.azi12
|
|
702
704
|
_o_3d, o_az = other._xTM3d, other.azi12
|
|
703
705
|
p = _MODS.formy.opposing(s_az, o_az, margin=tol)
|
|
704
|
-
if p is not None: # == p
|
|
706
|
+
if p is not None: # == isbool(p)
|
|
705
707
|
raise ValueError(_anti_(_parallel_) if p else _parallel_)
|
|
706
708
|
_diff = euclid # approximate length
|
|
707
709
|
_i3d3 = _intersect3d3 # NOT .vector3d.intersection3d3
|
|
@@ -723,7 +725,7 @@ class RhumbLineBase(_CapsBase):
|
|
|
723
725
|
|
|
724
726
|
P = GDict(lat1=self.lat1, lat2=p.lat, lat0=other.lat1,
|
|
725
727
|
lon1=self.lon1, lon2=p.lon, lon0=other.lon1,
|
|
726
|
-
name=
|
|
728
|
+
name=typename(self.Intersection, self.name))
|
|
727
729
|
r = self.Inverse(p.lat, p.lon, outmask=Caps.DISTANCE)
|
|
728
730
|
t = other.Inverse(p.lat, p.lon, outmask=Caps.DISTANCE)
|
|
729
731
|
P.set_(azi12= self.azi12, a12=r.a12, s12=r.s12,
|
|
@@ -805,7 +807,7 @@ class RhumbLineBase(_CapsBase):
|
|
|
805
807
|
'''DEPRECATED on 23.10.10, use method L{PlumbTo}.'''
|
|
806
808
|
P = self.PlumbTo(lat0, lon0, **exact_eps_est_tol)
|
|
807
809
|
r = _MODS.deprecated.classes.NearestOn4Tuple(P.lat2, P.lon2, P.s12, P.azi02,
|
|
808
|
-
name=self.nearestOn4
|
|
810
|
+
name=typename(self.nearestOn4))
|
|
809
811
|
r._iteration = P.iteration
|
|
810
812
|
return r
|
|
811
813
|
|
|
@@ -897,7 +899,7 @@ class RhumbLineBase(_CapsBase):
|
|
|
897
899
|
break
|
|
898
900
|
P.set_(azi0=r.azi1, a02=r.a12, s02=r.s12, # azi2=r.azi2,
|
|
899
901
|
lat0=lat0, lon0=lon0, iteration=i, at=r.azi2 - self.azi12,
|
|
900
|
-
name=
|
|
902
|
+
name=typename(self.PlumbTo, self.name))
|
|
901
903
|
except Exception as x: # Fsum(NAN) Value-, ZeroDivisionError
|
|
902
904
|
raise IntersectionError(lat0=lat0, lon0=lon0, tol=tol, exact=exact,
|
|
903
905
|
eps=eps, est=est, iteration=i, cause=x)
|
|
@@ -909,8 +911,8 @@ class RhumbLineBase(_CapsBase):
|
|
|
909
911
|
|
|
910
912
|
@arg s12: The distance along this rhumb line from its origin to the point
|
|
911
913
|
(C{meters}), can be negative.
|
|
912
|
-
@kwarg outmask: Bit-or'ed combination of L{Caps}
|
|
913
|
-
quantities to be returned.
|
|
914
|
+
@kwarg outmask: Bit-or'ed combination of L{Caps<pygeodesy.karney.Caps>}
|
|
915
|
+
values specifying the quantities to be returned.
|
|
914
916
|
|
|
915
917
|
@return: L{GDict} with 4 to 8 items C{azi12, a12, s12, S12, lat2, lat1,
|
|
916
918
|
lon2, lon1} with latitude C{lat2} and longitude C{lon2} of the
|
|
@@ -1046,7 +1048,7 @@ class _PseudoRhumbLine(RhumbLineBase):
|
|
|
1046
1048
|
|
|
1047
1049
|
__all__ += _ALL_DOCS(RhumbBase, RhumbLineBase)
|
|
1048
1050
|
|
|
1049
|
-
if __name__ ==
|
|
1051
|
+
if __name__ == _DMAIN_:
|
|
1050
1052
|
|
|
1051
1053
|
from pygeodesy import printf, Rhumb as Rh, RhumbAux as Ah
|
|
1052
1054
|
from pygeodesy.basics import _zip
|
pygeodesy/rhumb/ekx.py
CHANGED
|
@@ -43,7 +43,7 @@ from pygeodesy.utily import atan1, sincos2_
|
|
|
43
43
|
from math import asinh, atan, cos, cosh, radians, sin, sinh, sqrt, tan # as _tan
|
|
44
44
|
|
|
45
45
|
__all__ = _ALL_LAZY.rhumb_ekx
|
|
46
|
-
__version__ = '
|
|
46
|
+
__version__ = '25.04.12'
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
class Rhumb(RhumbBase):
|
|
@@ -263,11 +263,11 @@ class RhumbLine(RhumbLineBase):
|
|
|
263
263
|
@kwarg lat1: Latitude of the start point (C{degrees90}).
|
|
264
264
|
@kwarg lon1: Longitude of the start point (C{degrees180}).
|
|
265
265
|
@kwarg azi12: Azimuth of this rhumb line (compass C{degrees}).
|
|
266
|
-
@kwarg caps_name: Optional keyword arguments C{B{name}=NN} and
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
266
|
+
@kwarg caps_name: Optional keyword arguments C{B{name}=NN} and C{B{caps}=0},
|
|
267
|
+
a bit-or'ed combination of L{Caps<pygeodesy.karney.Caps>} values
|
|
268
|
+
specifying the required capabilities. Include C{Caps.LINE_OFF}
|
|
269
|
+
if updates to the B{C{rhumb}} should I{not be reflected} in this
|
|
270
|
+
rhumb line.
|
|
271
271
|
'''
|
|
272
272
|
RhumbLineBase.__init__(self, rhumb, lat1, lon1, azi12, **caps_name)
|
|
273
273
|
|
pygeodesy/rhumb/solve.py
CHANGED
|
@@ -11,7 +11,8 @@ as an (exact) rhumb or rhumb line from I{either GeographicLib 2.0 or 2.2+}.
|
|
|
11
11
|
from pygeodesy.basics import _xinstanceof
|
|
12
12
|
from pygeodesy.constants import _0_0, _180_0, _N_180_0, _over, _90_0 # PYCHOK used!
|
|
13
13
|
from pygeodesy.errors import RhumbError # PYCHOK used!
|
|
14
|
-
from pygeodesy.interns import NN, _a12_, _azi12_, _lat2_, _lon2_, _s12_,
|
|
14
|
+
from pygeodesy.interns import NN, _a12_, _azi12_, _DMAIN_, _lat2_, _lon2_, _s12_, \
|
|
15
|
+
_S12_, _UNDER_
|
|
15
16
|
from pygeodesy.karney import Caps, GDict, _norm180, Rhumb8Tuple, _sincos2d, _Xables
|
|
16
17
|
from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS
|
|
17
18
|
from pygeodesy.namedTuples import Destination3Tuple, Distance3Tuple
|
|
@@ -20,7 +21,7 @@ from pygeodesy.solveBase import _SolveGDictBase, _SolveGDictLineBase
|
|
|
20
21
|
from pygeodesy.utily import _unrollon, _Wrap, wrap360
|
|
21
22
|
|
|
22
23
|
__all__ = _ALL_LAZY.rhumb_solve
|
|
23
|
-
__version__ = '
|
|
24
|
+
__version__ = '25.04.12'
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
class _RhumbSolveBase(_SolveGDictBase):
|
|
@@ -29,7 +30,7 @@ class _RhumbSolveBase(_SolveGDictBase):
|
|
|
29
30
|
_Error = RhumbError
|
|
30
31
|
_Names_Direct = _lat2_, _lon2_, _S12_
|
|
31
32
|
_Names_Inverse = _azi12_, _s12_, _S12_
|
|
32
|
-
_Xable_name = _Xables.RhumbSolve.__name__
|
|
33
|
+
_Xable_name = _Xables.RhumbSolve.__name__ # typename
|
|
33
34
|
_Xable_path = _Xables.RhumbSolve()
|
|
34
35
|
|
|
35
36
|
@Property_RO
|
|
@@ -146,9 +147,9 @@ class RhumbSolve(_RhumbSolveBase):
|
|
|
146
147
|
@arg lat1: Latitude of the first point (C{degrees}).
|
|
147
148
|
@arg lon1: Longitude of the first point (C{degrees}).
|
|
148
149
|
@arg azi1: Azimuth at the first point (compass C{degrees}).
|
|
149
|
-
@kwarg caps: Bit-or'ed combination of L{Caps}
|
|
150
|
-
the capabilities the L{RhumbLineSolve}
|
|
151
|
-
should possess, always C{Caps.ALL}.
|
|
150
|
+
@kwarg caps: Bit-or'ed combination of L{Caps<pygeodesy.karney.Caps>}
|
|
151
|
+
values specifying the capabilities the L{RhumbLineSolve}
|
|
152
|
+
instance should possess, always C{Caps.ALL}.
|
|
152
153
|
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
153
154
|
|
|
154
155
|
@return: A L{RhumbLineSolve} instance.
|
|
@@ -247,18 +248,17 @@ class RhumbLineSolve(_RhumbSolveBase, _SolveGDictLineBase):
|
|
|
247
248
|
@arg lat1: Latitude of the first point (C{degrees90}).
|
|
248
249
|
@arg lon1: Longitude of the first point (C{degrees180}).
|
|
249
250
|
@arg azi12: Azimuth of the rhumb line (compass C{degrees180}).
|
|
250
|
-
@kwarg caps: Bit-or'ed combination of L{Caps}
|
|
251
|
-
the capabilities the L{RhumbLineSolve}
|
|
252
|
-
possess, always C{Caps.ALL}.
|
|
253
|
-
if updates to the B{C{rhumb}} should
|
|
254
|
-
reflected in this L{RhumbLineSolve} instance.
|
|
251
|
+
@kwarg caps: Bit-or'ed combination of L{Caps<pygeodesy.karney.Caps>}
|
|
252
|
+
values specifying the capabilities the L{RhumbLineSolve}
|
|
253
|
+
instance should possess, always C{Caps.ALL}. Include
|
|
254
|
+
C{Caps.LINE_OFF} if updates to the B{C{rhumb}} should
|
|
255
|
+
I{not be reflected} in this L{RhumbLineSolve} instance.
|
|
255
256
|
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
256
257
|
|
|
257
258
|
@kwarg name: Optional name (C{str}).
|
|
258
259
|
|
|
259
|
-
@raise RhumbError: Invalid path for C{RhumbSolve} executable or
|
|
260
|
-
|
|
261
|
-
property C{B{rhumb}.RhumbSolve}.
|
|
260
|
+
@raise RhumbError: Invalid path for C{RhumbSolve} executable or isn't the
|
|
261
|
+
C{RhumbSolve} executable, see property C{B{rhumb}.RhumbSolve}.
|
|
262
262
|
|
|
263
263
|
@raise TypeError: Invalid B{C{rhumb}}.
|
|
264
264
|
'''
|
|
@@ -355,7 +355,7 @@ class RhumbSolve7Tuple(Rhumb8Tuple):
|
|
|
355
355
|
|
|
356
356
|
__all__ += _ALL_DOCS(_RhumbSolveBase)
|
|
357
357
|
|
|
358
|
-
if __name__ ==
|
|
358
|
+
if __name__ == _DMAIN_:
|
|
359
359
|
|
|
360
360
|
from pygeodesy import printf
|
|
361
361
|
from sys import argv
|
pygeodesy/solveBase.py
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
u'''(INTERNAL) Private base classes for L{pygeodesy.geodsolve} and L{pygeodesy.rhumb.solve}.
|
|
5
5
|
'''
|
|
6
6
|
|
|
7
|
-
from pygeodesy.basics import clips, map2, _zip
|
|
7
|
+
from pygeodesy.basics import clips, _isin, map2, _zip
|
|
8
8
|
from pygeodesy.constants import DIG
|
|
9
9
|
from pygeodesy.datums import _earth_datum, _WGS84, _EWGS84
|
|
10
10
|
# from pygeodesy.ellipsoids import _EWGS84 # from .datums
|
|
@@ -23,7 +23,7 @@ from pygeodesy.units import Precision_
|
|
|
23
23
|
from pygeodesy.utily import unroll180, wrap360 # PYCHOK shared
|
|
24
24
|
|
|
25
25
|
__all__ = _ALL_LAZY.solveBase
|
|
26
|
-
__version__ = '
|
|
26
|
+
__version__ = '25.04.14'
|
|
27
27
|
|
|
28
28
|
_ERROR_ = 'ERROR'
|
|
29
29
|
|
|
@@ -211,7 +211,7 @@ class _SolveCapsBase(_CapsBase):
|
|
|
211
211
|
self._print(t)
|
|
212
212
|
try: # invoke and write to stdin
|
|
213
213
|
r, s = _popen2(cmd, stdin)
|
|
214
|
-
if len(r) < 6 or r[:5]
|
|
214
|
+
if len(r) < 6 or _isin(r[:5], _Error_, _ERROR_):
|
|
215
215
|
raise ValueError(r)
|
|
216
216
|
except (IOError, OSError, TypeError, ValueError) as x:
|
|
217
217
|
raise self._Error(cmd=t or _cmd_stdin_(cmd, stdin), cause=x)
|
pygeodesy/sphericalBase.py
CHANGED
|
@@ -12,7 +12,7 @@ U{Latitude/Longitude<https://www.Movable-Type.co.UK/scripts/latlong.html>}.
|
|
|
12
12
|
# make sure int/int division yields float quotient, see .basics
|
|
13
13
|
from __future__ import division as _; del _ # PYCHOK semicolon
|
|
14
14
|
|
|
15
|
-
from pygeodesy.basics import _copysign, isbool, isinstanceof, map1
|
|
15
|
+
from pygeodesy.basics import _copysign, isbool, _isin, isinstanceof, map1
|
|
16
16
|
from pygeodesy.cartesianBase import CartesianBase, Bearing2Tuple
|
|
17
17
|
from pygeodesy.constants import EPS, EPS0, PI, PI2, PI_2, R_M, \
|
|
18
18
|
_0_0, _0_5, _1_0, _180_0, _360_0, \
|
|
@@ -40,7 +40,7 @@ from pygeodesy.utily import acos1, asin1, atan2b, atan2d, degrees90, \
|
|
|
40
40
|
from math import cos, fabs, log, sin, sqrt
|
|
41
41
|
|
|
42
42
|
__all__ = _ALL_LAZY.sphericalBase
|
|
43
|
-
__version__ = '
|
|
43
|
+
__version__ = '25.04.14'
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
class CartesianSphericalBase(CartesianBase):
|
|
@@ -137,7 +137,7 @@ class LatLonSphericalBase(LatLonBase):
|
|
|
137
137
|
@raise TypeError: Invalid B{C{latlonh}} or B{C{datum}} not spherical.
|
|
138
138
|
'''
|
|
139
139
|
LatLonBase.__init__(self, latlonh, lon=lon, height=height, wrap=wrap, **name)
|
|
140
|
-
if
|
|
140
|
+
if not _isin(datum, None, self.datum):
|
|
141
141
|
self.datum = datum
|
|
142
142
|
|
|
143
143
|
def bearingTo2(self, other, wrap=False, raiser=False):
|
|
@@ -403,7 +403,7 @@ class LatLonSphericalBase(LatLonBase):
|
|
|
403
403
|
r = LatLonBase.rhumbDestination(self, distance, azimuth, exact=False, # Krüger
|
|
404
404
|
radius=radius, height=height, **name)
|
|
405
405
|
else: # radius=None from .rhumbMidpointTo
|
|
406
|
-
if radius
|
|
406
|
+
if _isin(radius, None, self._radius):
|
|
407
407
|
d, r = self.datum, radius
|
|
408
408
|
else:
|
|
409
409
|
d = _spherical_datum(radius, raiser=_radius_) # spherical only
|
|
@@ -557,7 +557,7 @@ class LatLonSphericalBase(LatLonBase):
|
|
|
557
557
|
b3 = fdot(map1(log, f1, f2, f3),
|
|
558
558
|
-b2, b1, b2 - b1) / f
|
|
559
559
|
|
|
560
|
-
d = self.datum if radius
|
|
560
|
+
d = self.datum if _isin(radius, None, self._radius) else \
|
|
561
561
|
_spherical_datum(radius, name=self.name, raiser=_radius_)
|
|
562
562
|
h = self._havg(other, h=height)
|
|
563
563
|
r = self.classof(degrees90(a3), degrees180(b3), datum=d, height=h, name=n)
|
|
@@ -674,7 +674,7 @@ def _m2radians(distance, radius, low=EPS): # PYCHOK in .spherical*
|
|
|
674
674
|
def _radians2m(rad, radius):
|
|
675
675
|
'''(INTERNAL) Angular distance in C{radians} to distance in C{meter}.
|
|
676
676
|
'''
|
|
677
|
-
if radius is not None: # not
|
|
677
|
+
if radius is not None: # not _isin(radius, None, _0_0)
|
|
678
678
|
rad *= R_M if radius is R_M else Radius(radius)
|
|
679
679
|
return rad
|
|
680
680
|
|
pygeodesy/sphericalNvector.py
CHANGED
|
@@ -33,13 +33,14 @@ to a normalised version of an (ECEF) cartesian coordinate.
|
|
|
33
33
|
# make sure int/int division yields float quosient, see .basics
|
|
34
34
|
from __future__ import division as _; del _ # PYCHOK semicolon
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
from pygeodesy.basics import _isin, _xinstanceof, typename
|
|
37
37
|
from pygeodesy.constants import EPS, EPS0, PI, PI2, PI_2, R_M, \
|
|
38
38
|
_0_0, _0_5, _1_0
|
|
39
39
|
# from pygeodesy.datums import Datums # from .sphericalBase
|
|
40
40
|
from pygeodesy.errors import PointsError, VectorError, _xError, _xkwds
|
|
41
41
|
from pygeodesy.fmath import fdot_, fmean, fsum
|
|
42
42
|
# from pygeodesy.fsums import fsum # from .fmath
|
|
43
|
+
# from pygeodesy.internals import typename # from .basics
|
|
43
44
|
from pygeodesy.interns import _composite_, _end_, _Nv00_, _other_, \
|
|
44
45
|
_point_, _pole_
|
|
45
46
|
from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, _ALL_OTHER
|
|
@@ -61,7 +62,7 @@ from pygeodesy.utily import atan2, degrees360, sincos2, sincos2_, sincos2d, \
|
|
|
61
62
|
# from math import fabs # from utily
|
|
62
63
|
|
|
63
64
|
__all__ = _ALL_LAZY.sphericalNvector
|
|
64
|
-
__version__ = '
|
|
65
|
+
__version__ = '25.04.14'
|
|
65
66
|
|
|
66
67
|
_lines_ = 'lines'
|
|
67
68
|
|
|
@@ -605,7 +606,7 @@ class LatLon(LatLonNvectorBase, LatLonSphericalBase):
|
|
|
605
606
|
return p2 # is point2 if not wrap
|
|
606
607
|
|
|
607
608
|
p = n.toLatLon(height=height or 0, LatLon=self.classof)
|
|
608
|
-
if height
|
|
609
|
+
if _isin(height, None, False): # interpolate height within extent
|
|
609
610
|
d = p1.distanceTo(p2)
|
|
610
611
|
f = (p1.distanceTo(p) / d) if d > EPS0 else _0_5
|
|
611
612
|
p.height = p1._havg(p2, f=max(_0_0, min(f, _1_0)))
|
|
@@ -1006,7 +1007,7 @@ def meanOf(points, height=None, wrap=False, **LatLon_and_kwds):
|
|
|
1006
1007
|
except (TypeError, ValueError) as x:
|
|
1007
1008
|
raise PointsError(points=points, wrap=wrap, cause=x, **LatLon_and_kwds)
|
|
1008
1009
|
return n.toLatLon(**_xkwds(LatLon_and_kwds, LatLon=LatLon, height=n.h,
|
|
1009
|
-
name=meanOf
|
|
1010
|
+
name=typename(meanOf)))
|
|
1010
1011
|
|
|
1011
1012
|
|
|
1012
1013
|
@deprecated_function
|
|
@@ -1050,7 +1051,7 @@ def nearestOn3(point, points, closed=False, radius=R_M, height=None, wrap=False)
|
|
|
1050
1051
|
|
|
1051
1052
|
@raise TypeError: Some B{C{points}} or B{C{point}} not C{LatLon}.
|
|
1052
1053
|
'''
|
|
1053
|
-
|
|
1054
|
+
_xinstanceof(LatLon, point=point)
|
|
1054
1055
|
|
|
1055
1056
|
return point.nearestOn3(points, closed=closed, radius=radius,
|
|
1056
1057
|
height=height, wrap=wrap)
|
|
@@ -16,7 +16,7 @@ U{Latitude/Longitude<https://www.Movable-Type.co.UK/scripts/latlong.html>}.
|
|
|
16
16
|
# make sure int/int division yields float quotient, see .basics
|
|
17
17
|
from __future__ import division as _; del _ # PYCHOK semicolon
|
|
18
18
|
|
|
19
|
-
from pygeodesy.basics import copysign0, map1, signOf
|
|
19
|
+
from pygeodesy.basics import copysign0, _isin, map1, signOf, typename
|
|
20
20
|
from pygeodesy.constants import EPS, EPS1, EPS4, PI, PI2, PI_2, PI_4, R_M, \
|
|
21
21
|
isnear0, isnear1, isnon0, _0_0, _0_5, \
|
|
22
22
|
_1_0, _2_0, _90_0
|
|
@@ -29,6 +29,7 @@ from pygeodesy.fsums import Fsum, fsum, fsumf_
|
|
|
29
29
|
from pygeodesy.formy import antipode_, bearing_, _bearingTo2, excessAbc_, \
|
|
30
30
|
excessGirard_, excessLHuilier_, opposing_, _radical2, \
|
|
31
31
|
vincentys_
|
|
32
|
+
# from pygeodesy.internals import typename # from .basics
|
|
32
33
|
from pygeodesy.interns import _1_, _2_, _coincident_, _composite_, _colinear_, \
|
|
33
34
|
_concentric_, _convex_, _end_, _infinite_, \
|
|
34
35
|
_invalid_, _line_, _near_, _null_, _parallel_, \
|
|
@@ -55,7 +56,7 @@ from pygeodesy.vector3d import sumOf, Vector3d
|
|
|
55
56
|
from math import asin, cos, degrees, fabs, radians, sin
|
|
56
57
|
|
|
57
58
|
__all__ = _ALL_LAZY.sphericalTrigonometry
|
|
58
|
-
__version__ = '
|
|
59
|
+
__version__ = '25.04.14'
|
|
59
60
|
|
|
60
61
|
_PI_EPS4 = PI - EPS4
|
|
61
62
|
if _PI_EPS4 >= PI:
|
|
@@ -1147,7 +1148,7 @@ def isPoleEnclosedBy(points, wrap=False): # PYCHOK no cover
|
|
|
1147
1148
|
def _LL3Tuple(lat, lon, height, where, LatLon, LatLon_kwds):
|
|
1148
1149
|
'''(INTERNAL) Helper for L{intersection}, L{intersections2} and L{meanOf}.
|
|
1149
1150
|
'''
|
|
1150
|
-
n = where
|
|
1151
|
+
n = typename(where)
|
|
1151
1152
|
if LatLon is None:
|
|
1152
1153
|
r = LatLon3Tuple(lat, lon, height, name=n)
|
|
1153
1154
|
else:
|
|
@@ -1250,7 +1251,7 @@ def nearestOn3(point, points, closed=False, radius=R_M, wrap=False, adjust=True,
|
|
|
1250
1251
|
adjust=adjust, limit=limit)
|
|
1251
1252
|
d = degrees2m(t.distance, radius=radius)
|
|
1252
1253
|
h = t.height
|
|
1253
|
-
n = nearestOn3
|
|
1254
|
+
n = typename(nearestOn3)
|
|
1254
1255
|
|
|
1255
1256
|
LL, kwds = _xkwds_pop2(LatLon_and_kwds, LatLon=LatLon)
|
|
1256
1257
|
r = LatLon3Tuple(t.lat, t.lon, h, name=n) if LL is None else \
|
|
@@ -1378,8 +1379,8 @@ def triangle8_(phiA, lamA, phiB, lamB, phiC, lamC, excess=excessAbc_,
|
|
|
1378
1379
|
C, _ = _A_r(c, *r)
|
|
1379
1380
|
|
|
1380
1381
|
D = fsumf_(PI2, -a, -b, -c) # deficit aka defect
|
|
1381
|
-
E = excessGirard_(A, B, C) if excess
|
|
1382
|
-
excessLHuilier_(a, b, c) if excess
|
|
1382
|
+
E = excessGirard_(A, B, C) if _isin(excess, excessGirard_, True) else (
|
|
1383
|
+
excessLHuilier_(a, b, c) if _isin(excess, excessLHuilier_, False) else
|
|
1383
1384
|
excessAbc_(*max((A, b, c), (B, c, a), (C, a, b))))
|
|
1384
1385
|
|
|
1385
1386
|
return Triangle8Tuple(A, a, B, b, C, c, D, E)
|
|
@@ -1388,7 +1389,7 @@ def triangle8_(phiA, lamA, phiB, lamB, phiC, lamC, excess=excessAbc_,
|
|
|
1388
1389
|
def _t7Tuple(t, radius):
|
|
1389
1390
|
'''(INTERNAL) Convert a L{Triangle8Tuple} to L{Triangle7Tuple}.
|
|
1390
1391
|
'''
|
|
1391
|
-
if radius: # not
|
|
1392
|
+
if radius: # not _isin(radius, None, _0_0)
|
|
1392
1393
|
r = radius if _isRadius(radius) else \
|
|
1393
1394
|
_ellipsoidal_datum(radius).ellipsoid.Rmean
|
|
1394
1395
|
A, B, C = map1(degrees, t.A, t.B, t.C)
|
pygeodesy/streprs.py
CHANGED
|
@@ -5,11 +5,11 @@ u'''Floating point and other formatting utilities.
|
|
|
5
5
|
'''
|
|
6
6
|
|
|
7
7
|
from pygeodesy.basics import isint, islistuple, isscalar, isstr, itemsorted, \
|
|
8
|
-
_zip, _0_0
|
|
8
|
+
_zip, _0_0, typename
|
|
9
9
|
# from pygeodesy.constants import _0_0
|
|
10
10
|
from pygeodesy.errors import _or, _IsnotError, _TypeError, _ValueError, \
|
|
11
11
|
_xkwds_get, _xkwds_item2
|
|
12
|
-
from pygeodesy.internals import
|
|
12
|
+
# from pygeodesy.internals import typename # from .basics
|
|
13
13
|
from pygeodesy.interns import NN, _0_, _0to9_, MISSING, _BAR_, _COMMASPACE_, \
|
|
14
14
|
_DOT_, _E_, _ELLIPSIS_, _EQUAL_, _H_, _LR_PAIRS, \
|
|
15
15
|
_N_, _name_, _not_scalar_, _PERCENT_, _SPACE_, \
|
|
@@ -22,7 +22,7 @@ from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS
|
|
|
22
22
|
from math import fabs, log10 as _log10
|
|
23
23
|
|
|
24
24
|
__all__ = _ALL_LAZY.streprs
|
|
25
|
-
__version__ = '
|
|
25
|
+
__version__ = '25.04.14'
|
|
26
26
|
|
|
27
27
|
_at_ = 'at' # PYCHOK used!
|
|
28
28
|
_EN_PREC = 6 # max MGRS/OSGR precision, 1 micrometer
|
|
@@ -82,7 +82,7 @@ class Fstr(str):
|
|
|
82
82
|
@raise ValueError: Invalid B{C{arg}}.
|
|
83
83
|
'''
|
|
84
84
|
def _error(arg):
|
|
85
|
-
n = _DOT_(Fstr
|
|
85
|
+
n = _DOT_(typename(Fstr), self.name or self)
|
|
86
86
|
return _SPACE_(n, _PERCENT_, repr(arg))
|
|
87
87
|
|
|
88
88
|
prec = 6 # default std %f and %F
|
|
@@ -105,7 +105,7 @@ class _Sub(str):
|
|
|
105
105
|
'''
|
|
106
106
|
# see .ellipsoidalNvector.LatLon.deltaTo
|
|
107
107
|
def __call__(self, *Classes):
|
|
108
|
-
t = _or(*(
|
|
108
|
+
t = _or(*map(typename, Classes))
|
|
109
109
|
return str.__mod__(self, t or MISSING)
|
|
110
110
|
|
|
111
111
|
|
|
@@ -148,9 +148,10 @@ class Fmt(object):
|
|
|
148
148
|
zone = _Fmt('%02d') # .epsg, .mgrs, .utmupsBase
|
|
149
149
|
|
|
150
150
|
def __init__(self):
|
|
151
|
-
for n, a in self.
|
|
151
|
+
for n, a in type(self).__dict__.items():
|
|
152
152
|
if isinstance(a, (Fstr, _Fmt)):
|
|
153
153
|
setattr(a, _name_, n)
|
|
154
|
+
self.__name__ = typename(type(self))
|
|
154
155
|
|
|
155
156
|
def __call__(self, obj, prec=9):
|
|
156
157
|
'''Return C{str(B{obj})} or C{repr(B{obj})}.
|
|
@@ -182,15 +183,14 @@ class Fmt(object):
|
|
|
182
183
|
'''
|
|
183
184
|
return self.ANGLE(_SPACE_((text or inst), _at_, hex(id(inst))))
|
|
184
185
|
|
|
185
|
-
Fmt
|
|
186
|
-
Fmt.__name__ = Fmt.__class__.__name__
|
|
186
|
+
Fmt = Fmt() # PYCHOK singleton
|
|
187
187
|
|
|
188
|
-
_DOTSTAR_ = Fmt.DOT(_STAR_)
|
|
188
|
+
_DOTSTAR_ = Fmt.DOT(_STAR_) # in _streprs above
|
|
189
189
|
# formats %G and %g drop all trailing zeros and the
|
|
190
190
|
# decimal point, making the float appear as an int
|
|
191
|
-
_Gg
|
|
192
|
-
_FfEeGg
|
|
193
|
-
_Fspec_
|
|
191
|
+
_Gg = (Fmt.G, Fmt.g)
|
|
192
|
+
_FfEeGg = (Fmt.F, Fmt.f, Fmt.E, Fmt.e) + _Gg # float formats
|
|
193
|
+
_Fspec_ = NN('[%[<flags>][<width>]', _DOTSTAR_, ']', _BAR_.join(_FfEeGg)) # in testStreprs
|
|
194
194
|
|
|
195
195
|
del _convergence_, _distant_, _e_, _eps_, _exceeds_, _EQUALSPACED_,\
|
|
196
196
|
_f_, _F_, _g_, _limit_, _PAREN_g, _RESIDUAL_
|
|
@@ -561,14 +561,14 @@ def unstr(where, *args, **kwds_):
|
|
|
561
561
|
t = reprs(args, fmt=g) if args else ()
|
|
562
562
|
if kwds:
|
|
563
563
|
t += pairs(itemsorted(kwds), fmt=g)
|
|
564
|
-
n = where if isstr(where) else
|
|
564
|
+
n = where if isstr(where) else typename(where) # _NN_
|
|
565
565
|
if C and hasattr(C, n):
|
|
566
566
|
try: # bound method of class C?
|
|
567
567
|
where = where.__func__
|
|
568
568
|
except AttributeError:
|
|
569
569
|
pass # method of C?
|
|
570
570
|
if getattr(C, n, None) is where:
|
|
571
|
-
n = _DOT_(
|
|
571
|
+
n = _DOT_(typename(C), n)
|
|
572
572
|
return Fmt.PAREN(n, _COMMASPACE_.join(t))
|
|
573
573
|
|
|
574
574
|
|
pygeodesy/trf.py
CHANGED
|
@@ -69,16 +69,18 @@ en/how-to-deal-with-etrs89-datum-and-time-dependent-transformation-parameters-45
|
|
|
69
69
|
@var RefFrames.WGS84g1762: RefFrame(name='WGS84g1762', epoch=2005, datum=Datums.GRS80) .Xforms=(0, 0)
|
|
70
70
|
'''
|
|
71
71
|
|
|
72
|
-
from pygeodesy.basics import map1, neg, isidentifier, isstr, _xinstanceof,
|
|
72
|
+
from pygeodesy.basics import _isin, map1, neg, isidentifier, isstr, _xinstanceof, \
|
|
73
|
+
_xscalar, typename
|
|
73
74
|
from pygeodesy.constants import _float as _F, _0_0s, _0_0, _0_001, _0_5, _1_0
|
|
74
75
|
from pygeodesy.datums import Datums, _earth_datum, _equall, _GDA2020_, _Names7, \
|
|
75
76
|
_negastr, Transform, _WGS84, _EWGS84, _operator
|
|
76
77
|
# from pygeodesy.ellipsoids import _EWGS84 # from .datums
|
|
77
78
|
from pygeodesy.errors import TRFError, _xattr, _xellipsoidall, _xkwds, _xkwds_item2
|
|
79
|
+
# from pygeodesy.internals import typename # from .basics
|
|
78
80
|
from pygeodesy.interns import MISSING, NN, _AT_, _COMMASPACE_, _conversion_, \
|
|
79
|
-
_datum_, _DOT_, _exists_, _invalid_, _MINUS_, \
|
|
80
|
-
_NAD83_, _no_, _PLUS_, _reframe_, _s_, _SPACE_, \
|
|
81
|
-
_STAR_,
|
|
81
|
+
_datum_, _DMAIN_, _DOT_, _exists_, _invalid_, _MINUS_, \
|
|
82
|
+
_NAD83_, _no_, _PLUS_, _reframe_, _s_, _SPACE_, _to_, \
|
|
83
|
+
_STAR_, _vs_, _WGS84_, _x_, _intern as _i
|
|
82
84
|
# from pygeodesy.lazily import _ALL_LAZY # from .units
|
|
83
85
|
from pygeodesy.named import ADict, classname, _lazyNamedEnumItem as _lazy, _name2__, \
|
|
84
86
|
_Named, _NamedEnum, _NamedEnumItem, _NamedTuple, Fmt, unstr
|
|
@@ -91,7 +93,7 @@ from math import ceil as _ceil, fabs
|
|
|
91
93
|
# import operator as _operator # from .datums
|
|
92
94
|
|
|
93
95
|
__all__ = _ALL_LAZY.trf
|
|
94
|
-
__version__ = '
|
|
96
|
+
__version__ = '25.04.14'
|
|
95
97
|
|
|
96
98
|
_EP0CH = Epoch(0, low=0)
|
|
97
99
|
_Es = {_EP0CH: _EP0CH} # L{Epoch}s, deleted below
|
|
@@ -176,9 +178,9 @@ class RefFrame(_NamedEnumItem):
|
|
|
176
178
|
|
|
177
179
|
@raise TypeError: Invalid B{C{datum}}.
|
|
178
180
|
'''
|
|
179
|
-
if datum
|
|
181
|
+
if _isin(datum, None, _GRS80):
|
|
180
182
|
pass
|
|
181
|
-
elif datum
|
|
183
|
+
elif _isin(datum, _WGS84, _EWGS84):
|
|
182
184
|
self._datum = _WGS84
|
|
183
185
|
else:
|
|
184
186
|
_earth_datum(self, datum, raiser=_datum_)
|
|
@@ -1062,8 +1064,8 @@ def _toRefFrame(point, reframe2, reframe=None, epoch=None,
|
|
|
1062
1064
|
e2 = e1 if epoch2 is None else Epoch(epoch2=epoch2)
|
|
1063
1065
|
t0 = _toTransform0(r1.name, e1, reframe2.name, e2)
|
|
1064
1066
|
if t0 is None:
|
|
1065
|
-
t = _SPACE_(RefFrame
|
|
1066
|
-
|
|
1067
|
+
t = _SPACE_(typename(RefFrame), _AT_(r1.name, e1),
|
|
1068
|
+
_to_, _AT_(reframe2.name, e2))
|
|
1067
1069
|
raise TRFError(_no_(_conversion_), txt=t)
|
|
1068
1070
|
|
|
1069
1071
|
name, LatLon_kwds = _name2__(name_LatLon_kwds)
|
|
@@ -1732,7 +1734,7 @@ trfXform(_WGS84g1150_, _ITRF2000_, epoch=_E(2004), xform=_P_0_0s, rates=_P_0_0s
|
|
|
1732
1734
|
|
|
1733
1735
|
del _E, _Es, _i, _P, _P_0_0s, _R, _Rs, _X, _Xs
|
|
1734
1736
|
|
|
1735
|
-
if __name__ ==
|
|
1737
|
+
if __name__ == _DMAIN_:
|
|
1736
1738
|
|
|
1737
1739
|
def _main():
|
|
1738
1740
|
from pygeodesy.basics import _args_kwds_names
|
|
@@ -1740,8 +1742,8 @@ if __name__ == '__main__':
|
|
|
1740
1742
|
from pygeodesy import printf
|
|
1741
1743
|
from time import localtime
|
|
1742
1744
|
|
|
1743
|
-
D = date2epoch
|
|
1744
|
-
E = epoch2date
|
|
1745
|
+
D = typename(date2epoch)
|
|
1746
|
+
E = typename(epoch2date)
|
|
1745
1747
|
y = localtime()[0]
|
|
1746
1748
|
for m in range(1, 13):
|
|
1747
1749
|
for d in (1, 15, _mDays[m] - 1, _mDays[m]):
|