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.
Files changed (96) hide show
  1. pygeodesy/__init__.py +30 -27
  2. pygeodesy/__main__.py +3 -3
  3. pygeodesy/albers.py +29 -36
  4. pygeodesy/auxilats/_CX_4.py +2 -2
  5. pygeodesy/auxilats/_CX_6.py +2 -2
  6. pygeodesy/auxilats/_CX_8.py +2 -2
  7. pygeodesy/auxilats/_CX_Rs.py +9 -9
  8. pygeodesy/auxilats/__init__.py +3 -3
  9. pygeodesy/auxilats/__main__.py +8 -6
  10. pygeodesy/auxilats/auxAngle.py +2 -2
  11. pygeodesy/auxilats/auxLat.py +5 -5
  12. pygeodesy/auxilats/auxily.py +5 -3
  13. pygeodesy/azimuthal.py +7 -6
  14. pygeodesy/basics.py +31 -17
  15. pygeodesy/booleans.py +12 -10
  16. pygeodesy/cartesianBase.py +21 -20
  17. pygeodesy/clipy.py +11 -10
  18. pygeodesy/constants.py +11 -10
  19. pygeodesy/css.py +14 -11
  20. pygeodesy/datums.py +8 -8
  21. pygeodesy/deprecated/bases.py +2 -2
  22. pygeodesy/deprecated/classes.py +2 -2
  23. pygeodesy/deprecated/consterns.py +4 -4
  24. pygeodesy/dms.py +8 -8
  25. pygeodesy/ecef.py +10 -7
  26. pygeodesy/elevations.py +9 -8
  27. pygeodesy/ellipsoidalBase.py +19 -8
  28. pygeodesy/ellipsoidalBaseDI.py +17 -15
  29. pygeodesy/ellipsoidalNvector.py +6 -3
  30. pygeodesy/ellipsoidalVincenty.py +4 -1
  31. pygeodesy/ellipsoids.py +167 -138
  32. pygeodesy/elliptic.py +9 -9
  33. pygeodesy/errors.py +44 -43
  34. pygeodesy/etm.py +7 -7
  35. pygeodesy/fmath.py +10 -9
  36. pygeodesy/formy.py +11 -12
  37. pygeodesy/frechet.py +216 -109
  38. pygeodesy/fstats.py +5 -4
  39. pygeodesy/fsums.py +78 -77
  40. pygeodesy/gars.py +4 -3
  41. pygeodesy/geodesici.py +15 -14
  42. pygeodesy/geodesicw.py +34 -32
  43. pygeodesy/geodesicx/__init__.py +1 -1
  44. pygeodesy/geodesicx/__main__.py +11 -9
  45. pygeodesy/geodesicx/gx.py +30 -33
  46. pygeodesy/geodesicx/gxarea.py +2 -2
  47. pygeodesy/geodesicx/gxline.py +5 -5
  48. pygeodesy/geodsolve.py +18 -17
  49. pygeodesy/geohash.py +5 -5
  50. pygeodesy/geoids.py +34 -31
  51. pygeodesy/hausdorff.py +17 -13
  52. pygeodesy/heights.py +2 -4
  53. pygeodesy/internals.py +28 -44
  54. pygeodesy/interns.py +10 -7
  55. pygeodesy/iters.py +8 -8
  56. pygeodesy/karney.py +68 -62
  57. pygeodesy/ktm.py +5 -5
  58. pygeodesy/latlonBase.py +14 -18
  59. pygeodesy/lazily.py +65 -63
  60. pygeodesy/lcc.py +11 -9
  61. pygeodesy/ltp.py +8 -7
  62. pygeodesy/ltpTuples.py +2 -2
  63. pygeodesy/mgrs.py +7 -6
  64. pygeodesy/named.py +47 -31
  65. pygeodesy/nvectorBase.py +7 -7
  66. pygeodesy/osgr.py +9 -8
  67. pygeodesy/points.py +12 -10
  68. pygeodesy/props.py +25 -25
  69. pygeodesy/resections.py +11 -10
  70. pygeodesy/rhumb/__init__.py +1 -1
  71. pygeodesy/rhumb/aux_.py +7 -7
  72. pygeodesy/rhumb/bases.py +22 -20
  73. pygeodesy/rhumb/ekx.py +6 -6
  74. pygeodesy/rhumb/solve.py +15 -15
  75. pygeodesy/solveBase.py +3 -3
  76. pygeodesy/sphericalBase.py +6 -6
  77. pygeodesy/sphericalNvector.py +6 -5
  78. pygeodesy/sphericalTrigonometry.py +8 -7
  79. pygeodesy/streprs.py +14 -14
  80. pygeodesy/trf.py +14 -12
  81. pygeodesy/triaxials.py +29 -26
  82. pygeodesy/units.py +5 -4
  83. pygeodesy/unitsBase.py +5 -4
  84. pygeodesy/ups.py +3 -3
  85. pygeodesy/utily.py +4 -4
  86. pygeodesy/utmups.py +4 -4
  87. pygeodesy/utmupsBase.py +88 -18
  88. pygeodesy/vector2d.py +18 -11
  89. pygeodesy/vector3d.py +7 -6
  90. pygeodesy/webmercator.py +6 -5
  91. pygeodesy/wgrs.py +6 -5
  92. {pygeodesy-25.4.8.dist-info → pygeodesy-25.4.25.dist-info}/METADATA +27 -23
  93. pygeodesy-25.4.25.dist-info/RECORD +118 -0
  94. pygeodesy-25.4.8.dist-info/RECORD +0 -118
  95. {pygeodesy-25.4.8.dist-info → pygeodesy-25.4.25.dist-info}/WHEEL +0 -0
  96. {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
- # from pygeodesy.basics import isscalar # from .points
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, _xkwds, _xkwds_get
86
- import pygeodesy.formy as _formy
87
- from pygeodesy.interns import NN, _DOT_, _n_, _units_
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, isscalar, PhiLam2Tuple, \
93
- points2 as _points2, radians
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__ = '24.12.31'
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 in (None, 1):
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{disance}.
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 expected
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 expected
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(self._ps1, fi1), p2(ps2, fi2))
224
+ return distance(p1(ps1, fi1), p2(ps2, fi2))
216
225
 
217
226
  try:
218
- return _frechet_(self._n1, f1, n2, f2, _dF, self.units)
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.__name__)
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
- return self._discrete(point2s, fraction, _formy._radistance(self))
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 _frechet_(ni, fi, nj, fj, dF, units): # MCCABE 14
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
- for intermediate B{C{point1s}} and B{C{point2s}}.
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
- return _frechet_(n1, 1, n2, 1, _dF, units)
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
- class Frechet6Tuple(_NamedTuple):
767
- '''6-Tuple C{(fd, fi1, fi2, r, n, units)} with the I{discrete}
768
- U{Fréchet<https://WikiPedia.org/wiki/Frechet_distance>} distance
769
- C{fd}, I{fractional} indices C{fi1} and C{fi2} as C{FIx}, the
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
- Use function L{fractional} to compute the point at a
781
- I{fractional} index.
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
- _Names_ = ('fd', 'fi1', 'fi2', 'r', _n_, _units_)
784
- _Units_ = (_Pass, FIx, FIx, Number_, Number_, _Pass)
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
- def toUnits(self, **Error_name): # PYCHOK expected
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
- # def __gt__(self, other):
793
- # _xinstanceof(Frechet6Tuple, other=other)
794
- # return self if self.fd > other.fd else other # PYCHOK .fd=[0]
795
- #
796
- # def __lt__(self, other):
797
- # _xinstanceof(Frechet6Tuple, other=other)
798
- # return self if self.fd < other.fd else other # PYCHOK .fd=[0]
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__ = '24.10.08'
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(name=self.__add__.__name__) # PYCHOK expected
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.__name__)
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