pygeodesy 25.4.8__py2.py3-none-any.whl → 25.4.25__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 +30 -27
- 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 +31 -17
- pygeodesy/booleans.py +12 -10
- pygeodesy/cartesianBase.py +21 -20
- 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 +10 -7
- 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 +167 -138
- pygeodesy/elliptic.py +9 -9
- pygeodesy/errors.py +44 -43
- pygeodesy/etm.py +7 -7
- pygeodesy/fmath.py +10 -9
- pygeodesy/formy.py +11 -12
- pygeodesy/frechet.py +216 -109
- pygeodesy/fstats.py +5 -4
- pygeodesy/fsums.py +78 -77
- 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 +14 -18
- pygeodesy/lazily.py +65 -63
- pygeodesy/lcc.py +11 -9
- pygeodesy/ltp.py +8 -7
- pygeodesy/ltpTuples.py +2 -2
- pygeodesy/mgrs.py +7 -6
- pygeodesy/named.py +47 -31
- pygeodesy/nvectorBase.py +7 -7
- pygeodesy/osgr.py +9 -8
- pygeodesy/points.py +12 -10
- pygeodesy/props.py +25 -25
- pygeodesy/resections.py +11 -10
- 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 +88 -18
- pygeodesy/vector2d.py +18 -11
- pygeodesy/vector3d.py +7 -6
- pygeodesy/webmercator.py +6 -5
- pygeodesy/wgrs.py +6 -5
- {pygeodesy-25.4.8.dist-info → pygeodesy-25.4.25.dist-info}/METADATA +27 -23
- pygeodesy-25.4.25.dist-info/RECORD +118 -0
- pygeodesy-25.4.8.dist-info/RECORD +0 -118
- {pygeodesy-25.4.8.dist-info → pygeodesy-25.4.25.dist-info}/WHEEL +0 -0
- {pygeodesy-25.4.8.dist-info → pygeodesy-25.4.25.dist-info}/top_level.txt +0 -0
pygeodesy/frechet.py
CHANGED
|
@@ -79,18 +79,20 @@ location and ordering of the points. Therefore, it is often a better metric
|
|
|
79
79
|
than the well-known C{Hausdorff} distance, see the L{hausdorff} module.
|
|
80
80
|
'''
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
from pygeodesy.basics import _isin, isscalar, typename
|
|
83
83
|
from pygeodesy.constants import EPS, EPS1, INF, NINF
|
|
84
84
|
from pygeodesy.datums import _ellipsoidal_datum, _WGS84
|
|
85
|
-
from pygeodesy.errors import PointsError, _xattr, _xcallable,
|
|
86
|
-
|
|
87
|
-
from pygeodesy
|
|
85
|
+
from pygeodesy.errors import PointsError, _xattr, _xcallable, \
|
|
86
|
+
_xkwds, _xkwds_get
|
|
87
|
+
# from pygeodesy import formy as _formy # _MODS.into
|
|
88
|
+
# from pygeodesy.internals import typename # from .basics
|
|
89
|
+
from pygeodesy.interns import _DMAIN_, _DOT_, _n_, NN, _units_
|
|
88
90
|
# from pygeodesy.iters import points2 as _points2 # from .points
|
|
89
|
-
from pygeodesy.lazily import _ALL_LAZY, _FOR_DOCS
|
|
91
|
+
from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, _FOR_DOCS
|
|
90
92
|
from pygeodesy.named import _name2__, _Named, _NamedTuple, _Pass
|
|
91
93
|
# from pygeodesy.namedTuples import PhiLam2Tuple # from .points
|
|
92
|
-
from pygeodesy.points import _distanceTo, _fractional,
|
|
93
|
-
points2 as _points2,
|
|
94
|
+
from pygeodesy.points import _distanceTo, _fractional, PhiLam2Tuple, \
|
|
95
|
+
points2 as _points2, radians
|
|
94
96
|
from pygeodesy.props import Property, property_doc_, property_RO
|
|
95
97
|
from pygeodesy.units import FIx, Float, Number_
|
|
96
98
|
from pygeodesy import unitsBase as _unitsBase # _Str_..., _xUnit, _xUnits
|
|
@@ -99,12 +101,14 @@ from collections import defaultdict as _defaultdict
|
|
|
99
101
|
# from math import radians # from .points
|
|
100
102
|
|
|
101
103
|
__all__ = _ALL_LAZY.frechet
|
|
102
|
-
__version__ = '
|
|
104
|
+
__version__ = '25.04.21'
|
|
105
|
+
|
|
106
|
+
_formy = _MODS.into(formy=__name__)
|
|
103
107
|
|
|
104
108
|
|
|
105
109
|
def _fraction(fraction, n):
|
|
106
110
|
f = 1 # int, no fractional indices
|
|
107
|
-
if fraction
|
|
111
|
+
if _isin(fraction, None, 1):
|
|
108
112
|
pass
|
|
109
113
|
elif not (isscalar(fraction) and EPS < fraction < EPS1
|
|
110
114
|
and (float(n) - fraction) < n):
|
|
@@ -180,7 +184,7 @@ class Frechet(_Named):
|
|
|
180
184
|
if d and d is not self._datum: # PYCHOK no cover
|
|
181
185
|
self._datum = _ellipsoidal_datum(d, name=self.name)
|
|
182
186
|
|
|
183
|
-
def discrete(self, point2s, fraction=None):
|
|
187
|
+
def discrete(self, point2s, fraction=None, recursive=False):
|
|
184
188
|
'''Compute the C{forward, discrete Fréchet} distance.
|
|
185
189
|
|
|
186
190
|
@arg point2s: Second set of points (C{LatLon}[], L{Numpy2LatLon}[],
|
|
@@ -189,36 +193,48 @@ class Frechet(_Named):
|
|
|
189
193
|
interpolate intermediate B{C{point2s}} or use
|
|
190
194
|
C{None}, C{0} or C{1} for no intermediate
|
|
191
195
|
B{C{point2s}} and no I{fractional} indices.
|
|
196
|
+
@kwarg recursive: Use C{True} for backward compatibility (C{bool})
|
|
197
|
+
or with I{fractional} indices.
|
|
192
198
|
|
|
193
199
|
@return: A L{Frechet6Tuple}C{(fd, fi1, fi2, r, n, units)}.
|
|
194
200
|
|
|
195
201
|
@raise FrechetError: Insufficient number of B{C{point2s}} or
|
|
196
|
-
an invalid B{C{point2}} or B{C{fraction}}
|
|
202
|
+
an invalid B{C{point2}} or B{C{fraction}}
|
|
203
|
+
or C{non-B{recursive}} and I{fractional}.
|
|
197
204
|
|
|
198
205
|
@raise RecursionError: Recursion depth exceeded, see U{sys.getrecursionlimit
|
|
199
|
-
<https://docs.Python.org/3/library/sys.html#sys.getrecursionlimit>}
|
|
206
|
+
<https://docs.Python.org/3/library/sys.html#sys.getrecursionlimit>},
|
|
207
|
+
only with C{B{recursive}=True}.
|
|
200
208
|
'''
|
|
201
|
-
return self._discrete(point2s, fraction, self.distance)
|
|
209
|
+
return self._discrete(point2s, fraction, self.distance, recursive)
|
|
202
210
|
|
|
203
|
-
def _discrete(self, point2s, fraction, distance):
|
|
204
|
-
'''(INTERNAL) Detailed C{discrete} with C{
|
|
211
|
+
def _discrete(self, point2s, fraction, distance, recursive):
|
|
212
|
+
'''(INTERNAL) Detailed C{discrete} with C{distance}.
|
|
205
213
|
'''
|
|
206
214
|
n2, ps2 = self._points2(point2s)
|
|
215
|
+
n1, ps1 = self._n1, self._ps1
|
|
207
216
|
|
|
208
217
|
f2 = _fraction(fraction, n2)
|
|
209
|
-
p2 = self.points_fraction if f2 < EPS1 else self.points_ # PYCHOK
|
|
218
|
+
p2 = self.points_fraction if f2 < EPS1 else self.points_ # PYCHOK attr
|
|
210
219
|
|
|
211
220
|
f1 = self.fraction
|
|
212
|
-
p1 = self.points_fraction if f1 < EPS1 else self.points_ # PYCHOK
|
|
221
|
+
p1 = self.points_fraction if f1 < EPS1 else self.points_ # PYCHOK attr
|
|
213
222
|
|
|
214
223
|
def _dF(fi1, fi2):
|
|
215
|
-
return distance(p1(
|
|
224
|
+
return distance(p1(ps1, fi1), p2(ps2, fi2))
|
|
216
225
|
|
|
217
226
|
try:
|
|
218
|
-
|
|
227
|
+
if recursive or not f1 == f2 == 1:
|
|
228
|
+
t = _frechetR(n1, f1, n2, f2, _dF, self.units)
|
|
229
|
+
else: # elif f1 == f2 == 1:
|
|
230
|
+
t = _frechetDP(n1, n2, _dF, self.units, False)
|
|
231
|
+
# else:
|
|
232
|
+
# f = fraction or self.fraction
|
|
233
|
+
# raise FrechetError(fraction=f, txt='non-recursive')
|
|
219
234
|
except TypeError as x:
|
|
220
|
-
t = _DOT_(self.classname, self.discrete
|
|
235
|
+
t = _DOT_(self.classname, typename(self.discrete))
|
|
221
236
|
raise FrechetError(t, cause=x)
|
|
237
|
+
return t
|
|
222
238
|
|
|
223
239
|
def distance(self, point1, point2):
|
|
224
240
|
'''Return the distance between B{C{point1}} and B{C{point2s}},
|
|
@@ -293,7 +309,7 @@ class Frechet(_Named):
|
|
|
293
309
|
|
|
294
310
|
@return: The interpolated, converted, intermediate B{C{points[fi]}}.
|
|
295
311
|
'''
|
|
296
|
-
return self.point(_fractional(points, fi, None, wrap=None)) # was=self.wrap
|
|
312
|
+
return self.point(_fractional(points, fi, None, wrap=None, dup=True)) # was=self.wrap
|
|
297
313
|
|
|
298
314
|
def _points2(self, points):
|
|
299
315
|
'''(INTERNAL) Check a set of points, overloaded in L{FrechetDistanceTo}.
|
|
@@ -368,15 +384,16 @@ class _FrechetMeterRadians(Frechet):
|
|
|
368
384
|
_units = _unitsBase._Str_meter
|
|
369
385
|
_units_ = _unitsBase._Str_radians
|
|
370
386
|
|
|
371
|
-
def discrete(self, point2s, fraction=None):
|
|
387
|
+
def discrete(self, point2s, fraction=None, recursive=False):
|
|
372
388
|
'''Overloaded method L{Frechet.discrete} to determine
|
|
373
389
|
the distance function and units from the optional
|
|
374
390
|
keyword arguments given at this instantiation, see
|
|
375
391
|
property C{kwds}.
|
|
376
392
|
|
|
377
|
-
@see: L{Frechet.discrete} for other details.
|
|
393
|
+
@see: Method L{Frechet.discrete} for other details.
|
|
378
394
|
'''
|
|
379
|
-
|
|
395
|
+
_rad = _formy._radistance(self)
|
|
396
|
+
return self._discrete(point2s, fraction, _rad, recursive)
|
|
380
397
|
|
|
381
398
|
@Property
|
|
382
399
|
def _func_(self): # see _formy._radistance
|
|
@@ -651,6 +668,44 @@ class FrechetThomas(_FrechetMeterRadians):
|
|
|
651
668
|
discrete = Frechet.discrete
|
|
652
669
|
|
|
653
670
|
|
|
671
|
+
class Frechet6Tuple(_NamedTuple):
|
|
672
|
+
'''6-Tuple C{(fd, fi1, fi2, r, n, units)} with the I{discrete}
|
|
673
|
+
U{Fréchet<https://WikiPedia.org/wiki/Frechet_distance>} distance
|
|
674
|
+
C{fd}, I{fractional} indices C{fi1} and C{fi2} as C{FIx}, the
|
|
675
|
+
recursion depth C{r}, the number of distances computed C{n} and
|
|
676
|
+
the L{units} class or name of the distance C{units}.
|
|
677
|
+
|
|
678
|
+
Empirically, the recursion depth C{r ≈ 2 * sqrt(len(point1s) *
|
|
679
|
+
len(point2s))} or C{0} if non-recursive, see function L{frechet_}.
|
|
680
|
+
|
|
681
|
+
If I{fractional} indices C{fi1} and C{fi2} are C{int}, the
|
|
682
|
+
returned C{fd} is the distance between C{point1s[fi1]} and
|
|
683
|
+
C{point2s[fi2]}. For C{float} indices, the distance is
|
|
684
|
+
between an intermediate point along C{point1s[int(fi1)]} and
|
|
685
|
+
C{point1s[int(fi1) + 1]} respectively an intermediate point
|
|
686
|
+
along C{point2s[int(fi2)]} and C{point2s[int(fi2) + 1]}.
|
|
687
|
+
|
|
688
|
+
Use function L{fractional} to compute the point at a
|
|
689
|
+
I{fractional} index.
|
|
690
|
+
'''
|
|
691
|
+
_Names_ = ('fd', 'fi1', 'fi2', 'r', _n_, _units_)
|
|
692
|
+
_Units_ = (_Pass, FIx, FIx, Number_, Number_, _Pass)
|
|
693
|
+
|
|
694
|
+
def toUnits(self, **Error_name): # PYCHOK expected
|
|
695
|
+
'''Overloaded C{_NamedTuple.toUnits} for C{fd} units.
|
|
696
|
+
'''
|
|
697
|
+
U = _unitsBase._xUnit(self.units, Float) # PYCHOK expected
|
|
698
|
+
return _NamedTuple.toUnits(self.reUnit(U), **Error_name) # PYCHOK self
|
|
699
|
+
|
|
700
|
+
# def __gt__(self, other):
|
|
701
|
+
# _xinstanceof(Frechet6Tuple, other=other)
|
|
702
|
+
# return self if self.fd > other.fd else other # PYCHOK .fd=[0]
|
|
703
|
+
#
|
|
704
|
+
# def __lt__(self, other):
|
|
705
|
+
# _xinstanceof(Frechet6Tuple, other=other)
|
|
706
|
+
# return self if self.fd < other.fd else other # PYCHOK .fd=[0]
|
|
707
|
+
|
|
708
|
+
|
|
654
709
|
class FrechetVincentys(_FrechetMeterRadians):
|
|
655
710
|
'''Compute the C{Frechet} distance with function L{pygeodesy.vincentys_}.
|
|
656
711
|
|
|
@@ -674,59 +729,7 @@ class FrechetVincentys(_FrechetMeterRadians):
|
|
|
674
729
|
discrete = Frechet.discrete
|
|
675
730
|
|
|
676
731
|
|
|
677
|
-
def
|
|
678
|
-
'''(INTERNAL) Recursive core of function L{frechet_}
|
|
679
|
-
and method C{discrete} of C{Frechet...} classes.
|
|
680
|
-
'''
|
|
681
|
-
iFs = {}
|
|
682
|
-
|
|
683
|
-
def iF(i): # cache index, depth ints and floats
|
|
684
|
-
return iFs.setdefault(i, i)
|
|
685
|
-
|
|
686
|
-
cF = _defaultdict(dict)
|
|
687
|
-
|
|
688
|
-
def _rF(i, j, r): # recursive Fréchet
|
|
689
|
-
i = iF(i)
|
|
690
|
-
j = iF(j)
|
|
691
|
-
try:
|
|
692
|
-
t = cF[i][j]
|
|
693
|
-
except KeyError:
|
|
694
|
-
r = iF(r + 1)
|
|
695
|
-
try:
|
|
696
|
-
if i > 0:
|
|
697
|
-
if j > 0:
|
|
698
|
-
t = min(_rF(i - fi, j, r),
|
|
699
|
-
_rF(i - fi, j - fj, r),
|
|
700
|
-
_rF(i, j - fj, r))
|
|
701
|
-
elif j < 0:
|
|
702
|
-
raise IndexError
|
|
703
|
-
else: # j == 0
|
|
704
|
-
t = _rF(i - fi, 0, r)
|
|
705
|
-
elif i < 0:
|
|
706
|
-
raise IndexError
|
|
707
|
-
|
|
708
|
-
elif j > 0: # i == 0
|
|
709
|
-
t = _rF(0, j - fj, r)
|
|
710
|
-
elif j < 0: # i == 0
|
|
711
|
-
raise IndexError
|
|
712
|
-
else: # i == j == 0
|
|
713
|
-
t = (NINF, i, j, r)
|
|
714
|
-
|
|
715
|
-
d = dF(i, j)
|
|
716
|
-
if d > t[0]:
|
|
717
|
-
t = (d, i, j, r)
|
|
718
|
-
except IndexError:
|
|
719
|
-
t = (INF, i, j, r)
|
|
720
|
-
cF[i][j] = t
|
|
721
|
-
return t
|
|
722
|
-
|
|
723
|
-
t = _rF(ni - 1, nj - 1, 0)
|
|
724
|
-
t += (sum(map(len, cF.values())), units)
|
|
725
|
-
# del cF, iFs
|
|
726
|
-
return Frechet6Tuple(t) # *t
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
def frechet_(point1s, point2s, distance=None, units=NN):
|
|
732
|
+
def frechet_(point1s, point2s, distance=None, units=NN, recursive=False):
|
|
730
733
|
'''Compute the I{discrete} U{Fréchet<https://WikiPedia.org/wiki/Frechet_distance>}
|
|
731
734
|
distance between two paths, each given as a set of points.
|
|
732
735
|
|
|
@@ -737,6 +740,7 @@ def frechet_(point1s, point2s, distance=None, units=NN):
|
|
|
737
740
|
@kwarg distance: Callable returning the distance between a B{C{point1s}}
|
|
738
741
|
and a B{C{point2s}} point (signature C{(point1, point2)}).
|
|
739
742
|
@kwarg units: Optional, the distance units (C{Unit} or C{str}).
|
|
743
|
+
@kwarg recursive: Use C{True} for backward compatibility (C{bool}).
|
|
740
744
|
|
|
741
745
|
@return: A L{Frechet6Tuple}C{(fd, fi1, fi2, r, n, units)} where C{fi1}
|
|
742
746
|
and C{fi2} are type C{int} indices into B{C{point1s}} respectively
|
|
@@ -745,12 +749,16 @@ def frechet_(point1s, point2s, distance=None, units=NN):
|
|
|
745
749
|
@raise FrechetError: Insufficient number of B{C{point1s}} or B{C{point2s}}.
|
|
746
750
|
|
|
747
751
|
@raise RecursionError: Recursion depth exceeded, see U{sys.getrecursionlimit()
|
|
748
|
-
<https://docs.Python.org/3/library/sys.html#sys.getrecursionlimit>}
|
|
752
|
+
<https://docs.Python.org/3/library/sys.html#sys.getrecursionlimit>},
|
|
753
|
+
only with C{B{recursive}=True}.
|
|
749
754
|
|
|
750
755
|
@raise TypeError: If B{C{distance}} is not a callable.
|
|
751
756
|
|
|
752
|
-
@note: Function L{frechet_} does I{not} support I{fractional} indices
|
|
753
|
-
|
|
757
|
+
@note: Function L{frechet_} does I{not} support I{fractional} indices for intermediate
|
|
758
|
+
B{C{point1s}} and B{C{point2s}}.
|
|
759
|
+
|
|
760
|
+
@see: Non-recursive U{dp_frechet_dist
|
|
761
|
+
<https://GitHub.com/cjekel/similarity_measures/issues/6#issuecomment-544350039>}.
|
|
754
762
|
'''
|
|
755
763
|
_xcallable(distance=distance)
|
|
756
764
|
|
|
@@ -760,42 +768,141 @@ def frechet_(point1s, point2s, distance=None, units=NN):
|
|
|
760
768
|
def _dF(i1, i2):
|
|
761
769
|
return distance(ps1[i1], ps2[i2])
|
|
762
770
|
|
|
763
|
-
|
|
771
|
+
if recursive:
|
|
772
|
+
t = _frechetR(n1, 1, n2, 1, _dF, units)
|
|
773
|
+
else:
|
|
774
|
+
s = n1 < n2
|
|
775
|
+
if s: # n2, ps2 as shortest
|
|
776
|
+
n1, ps1, n2, ps2 = n2, ps2, n1, ps1
|
|
777
|
+
t = _frechetDP(n1, n2, _dF, units, s)
|
|
778
|
+
return t
|
|
764
779
|
|
|
765
780
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
recursion depth C{r}, the number of distances computed C{n} and
|
|
771
|
-
the L{units} class or class or name of the distance C{units}.
|
|
781
|
+
def _frechet3(r, t, s): # return tuple r if equal
|
|
782
|
+
if s[0] < r[0]:
|
|
783
|
+
r = s
|
|
784
|
+
return t if t[0] < r[0] else r
|
|
772
785
|
|
|
773
|
-
If I{fractional} indices C{fi1} and C{fi2} are C{int}, the
|
|
774
|
-
returned C{fd} is the distance between C{point1s[fi1]} and
|
|
775
|
-
C{point2s[fi2]}. For C{float} indices, the distance is
|
|
776
|
-
between an intermediate point along C{point1s[int(fi1)]} and
|
|
777
|
-
C{point1s[int(fi1) + 1]} respectively an intermediate point
|
|
778
|
-
along C{point2s[int(fi2)]} and C{point2s[int(fi2) + 1]}.
|
|
779
786
|
|
|
780
|
-
|
|
781
|
-
|
|
787
|
+
def _frechetDP(ni, nj, dF, units, swap):
|
|
788
|
+
'''(INTERNAL) DP core of function L{frechet_} and
|
|
789
|
+
method C{discrete} of C{Frechet...} classes.
|
|
782
790
|
'''
|
|
783
|
-
|
|
784
|
-
|
|
791
|
+
def _max2(r, *t): # return tuple r if equal
|
|
792
|
+
return t if t[0] > r[0] else r
|
|
793
|
+
|
|
794
|
+
_min3 = _frechet3
|
|
795
|
+
|
|
796
|
+
d = dF(0, 0)
|
|
797
|
+
t = (d, 0, 0)
|
|
798
|
+
r = [t] * nj # nj-list of 3-tuples
|
|
799
|
+
for j in range(1, nj):
|
|
800
|
+
d = max(d, dF(0, j))
|
|
801
|
+
r[j] = (d, 0, j)
|
|
802
|
+
for i in range(1, ni):
|
|
803
|
+
t1j1 = r[0] # == r[i-1][0]
|
|
804
|
+
r[0] = t = _max2(t1j1, dF(i, 0), i, 0)
|
|
805
|
+
for j in range(1, nj):
|
|
806
|
+
t1j = r[j] # == r[i-1][j]
|
|
807
|
+
t = _min3(t1j, t, t1j1) # == r[i-1][j-1]
|
|
808
|
+
r[j] = t = _max2(t, dF(i, j), i, j)
|
|
809
|
+
t1j1 = t1j
|
|
810
|
+
d, i, j = t # == r[nj-1]
|
|
811
|
+
if swap:
|
|
812
|
+
i, j = j, i
|
|
813
|
+
# del r
|
|
814
|
+
return Frechet6Tuple(d, i, j, 0, (ni * nj), units)
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
def _frechetR(ni, fi, nj, fj, dF, units): # MCCABE 14
|
|
818
|
+
'''(INTERNAL) Recursive core of function L{frechet_}
|
|
819
|
+
and method C{discrete} of C{Frechet...} classes.
|
|
820
|
+
'''
|
|
821
|
+
class iF(dict): # index, depth ints and floats cache
|
|
822
|
+
def __call__(self, i):
|
|
823
|
+
return dict.setdefault(self, i, i)
|
|
785
824
|
|
|
786
|
-
|
|
787
|
-
'''Overloaded C{_NamedTuple.toUnits} for C{fd} units.
|
|
788
|
-
'''
|
|
789
|
-
U = _unitsBase._xUnit(self.units, Float) # PYCHOK expected
|
|
790
|
-
return _NamedTuple.toUnits(self.reUnit(U), **Error_name) # PYCHOK self
|
|
825
|
+
_min3 = _frechet3
|
|
791
826
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
#
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
827
|
+
iF = iF() # PYCHOK once
|
|
828
|
+
tF = _defaultdict(dict) # tuple[i][j]
|
|
829
|
+
|
|
830
|
+
def _rF(i, j, r): # recursive Fréchet
|
|
831
|
+
i = iF(i)
|
|
832
|
+
j = iF(j)
|
|
833
|
+
try:
|
|
834
|
+
return tF[i][j]
|
|
835
|
+
except KeyError:
|
|
836
|
+
pass
|
|
837
|
+
r = iF(r + 1)
|
|
838
|
+
try:
|
|
839
|
+
if i > 0:
|
|
840
|
+
if j > 0:
|
|
841
|
+
t = _min3(_rF(i - fi, j, r),
|
|
842
|
+
_rF(i - fi, j - fj, r),
|
|
843
|
+
_rF(i, j - fj, r))
|
|
844
|
+
elif j < 0:
|
|
845
|
+
raise IndexError
|
|
846
|
+
else: # j == 0
|
|
847
|
+
t = _rF(i - fi, 0, r)
|
|
848
|
+
elif i < 0:
|
|
849
|
+
raise IndexError
|
|
850
|
+
|
|
851
|
+
elif j > 0: # i == 0
|
|
852
|
+
t = _rF(0, j - fj, r)
|
|
853
|
+
elif j < 0: # i == 0
|
|
854
|
+
raise IndexError
|
|
855
|
+
else: # i == j == 0
|
|
856
|
+
t = (NINF, i, j, r)
|
|
857
|
+
|
|
858
|
+
d = dF(i, j)
|
|
859
|
+
if d > t[0]:
|
|
860
|
+
t = (d, i, j, r)
|
|
861
|
+
except IndexError:
|
|
862
|
+
t = (INF, i, j, r)
|
|
863
|
+
tF[i][j] = t
|
|
864
|
+
return t
|
|
865
|
+
|
|
866
|
+
d, i, j, r = _rF(ni - 1, nj - 1, 0)
|
|
867
|
+
n = sum(map(len, tF.values())) # (ni * nj) <= n < (ni * nj * 2)
|
|
868
|
+
# del iF, tF
|
|
869
|
+
return Frechet6Tuple(d, i, j, r, n, units)
|
|
870
|
+
|
|
871
|
+
|
|
872
|
+
if __name__ == _DMAIN_:
|
|
873
|
+
|
|
874
|
+
def _main():
|
|
875
|
+
from time import time
|
|
876
|
+
from pygeodesy import euclid, printf # randomrangenerator
|
|
877
|
+
_r = range # randomrangenerator('R')
|
|
878
|
+
|
|
879
|
+
def _d(p1, p2):
|
|
880
|
+
return euclid(p1[0] - p2[0], p1[1] - p2[1])
|
|
881
|
+
|
|
882
|
+
p1 = tuple(zip(_r(-90, 90, 4), _r(-180, 180, 8))) # 45
|
|
883
|
+
p2 = tuple(zip(_r(-89, 90, 3), _r(-179, 180, 6))) # 60
|
|
884
|
+
ss = []
|
|
885
|
+
for r, u in ((True, 'R'), (False, 'DP')):
|
|
886
|
+
s = time()
|
|
887
|
+
t = frechet_(p1, p2, _d, recursive=r, units=u)
|
|
888
|
+
s = time() - s
|
|
889
|
+
printf('# %r in %.3f ms', t, (s * 1e3))
|
|
890
|
+
ss.append(s)
|
|
891
|
+
|
|
892
|
+
from pygeodesy.internals import _versions
|
|
893
|
+
printf('# %s %.2fX', _versions(), (ss[0] / ss[1]))
|
|
894
|
+
|
|
895
|
+
_main()
|
|
896
|
+
|
|
897
|
+
# % python3.13 -m pygeodesy.frechet
|
|
898
|
+
# Frechet6Tuple(fd=3.828427, fi1=2, fi2=3, r=99, n=2700, units='R') in 3.575 ms
|
|
899
|
+
# Frechet6Tuple(fd=3.828427, fi1=2, fi2=3, r=0, n=2700, units='DP') in 0.704 ms
|
|
900
|
+
# pygeodesy 25.4.25 Python 3.13.3 64bit arm64 macOS 15.4.1 5.08X
|
|
901
|
+
|
|
902
|
+
# % python2.7 -m pygeodesy.frechet
|
|
903
|
+
# Frechet6Tuple(fd=3.828427, fi1=2, fi2=3, r=99, n=2700, units='R') in 7.030 ms
|
|
904
|
+
# Frechet6Tuple(fd=3.828427, fi1=2, fi2=3, r=0, n=2700, units='DP') in 1.536 ms
|
|
905
|
+
# pygeodesy 25.4.25 Python 2.7.18 64bit arm64_x86_64 macOS 10.16 4.58X
|
|
799
906
|
|
|
800
907
|
# **) MIT License
|
|
801
908
|
#
|
pygeodesy/fstats.py
CHANGED
|
@@ -9,12 +9,13 @@ multiplication.
|
|
|
9
9
|
from __future__ import division as _; del _ # PYCHOK semicolon
|
|
10
10
|
|
|
11
11
|
from pygeodesy.basics import isscalar, isodd, _xinstanceof, \
|
|
12
|
-
_xiterable, _xsubclassof, _zip
|
|
12
|
+
_xiterable, _xsubclassof, _zip, typename
|
|
13
13
|
from pygeodesy.constants import _0_0, _1_0, _2_0, _3_0, _4_0, _6_0
|
|
14
14
|
from pygeodesy.errors import _ValueError, _xError, _xkwds_item2, \
|
|
15
15
|
_xsError
|
|
16
16
|
from pygeodesy.fmath import Fsqrt, Fmt
|
|
17
17
|
from pygeodesy.fsums import _2finite, Fsum, _iadd_op_, _isFsum_2Tuple
|
|
18
|
+
# from pygeodesy.internals import typename # from .basics
|
|
18
19
|
from pygeodesy.interns import _odd_, _SPACE_
|
|
19
20
|
from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY
|
|
20
21
|
from pygeodesy.named import _name__, _Named, _NotImplemented, \
|
|
@@ -23,7 +24,7 @@ from pygeodesy.named import _name__, _Named, _NotImplemented, \
|
|
|
23
24
|
# from pygeodesy.streprs import Fmt # from .fmath
|
|
24
25
|
|
|
25
26
|
__all__ = _ALL_LAZY.fstats
|
|
26
|
-
__version__ = '
|
|
27
|
+
__version__ = '25.04.14'
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
def _sampled(n, sample):
|
|
@@ -53,7 +54,7 @@ class _FstatsNamed(_Named):
|
|
|
53
54
|
'''Sum of this and an other instance, a C{scalar}, an L{Fsum}
|
|
54
55
|
or L{Fsum2Tuple}.
|
|
55
56
|
'''
|
|
56
|
-
f = self.copy(
|
|
57
|
+
f = self.copy(name__=self.__add__) # PYCHOK expected
|
|
57
58
|
f += other
|
|
58
59
|
return f
|
|
59
60
|
|
|
@@ -742,7 +743,7 @@ class Flinear(_FstatsNamed):
|
|
|
742
743
|
@see: Method C{Flinear.fadd} for further details.
|
|
743
744
|
'''
|
|
744
745
|
if isodd(len(x_ys)):
|
|
745
|
-
t = _SPACE_(_odd_, len
|
|
746
|
+
t = _SPACE_(_odd_, typename(len))
|
|
746
747
|
raise _ValueError(t, len(x_ys))
|
|
747
748
|
return self.fadd(x_ys[0::2], x_ys[1::2], **sample)
|
|
748
749
|
|