pygeodesy 24.3.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.
Files changed (115) hide show
  1. PyGeodesy-24.3.24.dist-info/METADATA +272 -0
  2. PyGeodesy-24.3.24.dist-info/RECORD +115 -0
  3. PyGeodesy-24.3.24.dist-info/WHEEL +6 -0
  4. PyGeodesy-24.3.24.dist-info/top_level.txt +1 -0
  5. pygeodesy/LICENSE +21 -0
  6. pygeodesy/__init__.py +615 -0
  7. pygeodesy/__main__.py +103 -0
  8. pygeodesy/albers.py +867 -0
  9. pygeodesy/auxilats/_CX_4.py +218 -0
  10. pygeodesy/auxilats/_CX_6.py +314 -0
  11. pygeodesy/auxilats/_CX_8.py +475 -0
  12. pygeodesy/auxilats/__init__.py +54 -0
  13. pygeodesy/auxilats/__main__.py +86 -0
  14. pygeodesy/auxilats/auxAngle.py +548 -0
  15. pygeodesy/auxilats/auxDLat.py +302 -0
  16. pygeodesy/auxilats/auxDST.py +296 -0
  17. pygeodesy/auxilats/auxLat.py +848 -0
  18. pygeodesy/auxilats/auxily.py +272 -0
  19. pygeodesy/azimuthal.py +1150 -0
  20. pygeodesy/basics.py +892 -0
  21. pygeodesy/booleans.py +2031 -0
  22. pygeodesy/cartesianBase.py +1062 -0
  23. pygeodesy/clipy.py +704 -0
  24. pygeodesy/constants.py +516 -0
  25. pygeodesy/css.py +660 -0
  26. pygeodesy/datums.py +752 -0
  27. pygeodesy/deprecated/__init__.py +61 -0
  28. pygeodesy/deprecated/bases.py +40 -0
  29. pygeodesy/deprecated/classes.py +262 -0
  30. pygeodesy/deprecated/consterns.py +54 -0
  31. pygeodesy/deprecated/datum.py +40 -0
  32. pygeodesy/deprecated/functions.py +375 -0
  33. pygeodesy/deprecated/nvector.py +48 -0
  34. pygeodesy/deprecated/rhumbBase.py +32 -0
  35. pygeodesy/deprecated/rhumbaux.py +33 -0
  36. pygeodesy/deprecated/rhumbsolve.py +33 -0
  37. pygeodesy/deprecated/rhumbx.py +33 -0
  38. pygeodesy/dms.py +986 -0
  39. pygeodesy/ecef.py +1348 -0
  40. pygeodesy/elevations.py +279 -0
  41. pygeodesy/ellipsoidalBase.py +1224 -0
  42. pygeodesy/ellipsoidalBaseDI.py +913 -0
  43. pygeodesy/ellipsoidalExact.py +343 -0
  44. pygeodesy/ellipsoidalGeodSolve.py +343 -0
  45. pygeodesy/ellipsoidalKarney.py +403 -0
  46. pygeodesy/ellipsoidalNvector.py +685 -0
  47. pygeodesy/ellipsoidalVincenty.py +590 -0
  48. pygeodesy/ellipsoids.py +2476 -0
  49. pygeodesy/elliptic.py +1198 -0
  50. pygeodesy/epsg.py +243 -0
  51. pygeodesy/errors.py +804 -0
  52. pygeodesy/etm.py +1190 -0
  53. pygeodesy/fmath.py +1013 -0
  54. pygeodesy/formy.py +1818 -0
  55. pygeodesy/frechet.py +865 -0
  56. pygeodesy/fstats.py +760 -0
  57. pygeodesy/fsums.py +1898 -0
  58. pygeodesy/gars.py +358 -0
  59. pygeodesy/geodesicw.py +581 -0
  60. pygeodesy/geodesicx/_C4_24.py +1699 -0
  61. pygeodesy/geodesicx/_C4_27.py +2395 -0
  62. pygeodesy/geodesicx/_C4_30.py +3301 -0
  63. pygeodesy/geodesicx/__init__.py +48 -0
  64. pygeodesy/geodesicx/__main__.py +91 -0
  65. pygeodesy/geodesicx/gx.py +1382 -0
  66. pygeodesy/geodesicx/gxarea.py +535 -0
  67. pygeodesy/geodesicx/gxbases.py +154 -0
  68. pygeodesy/geodesicx/gxline.py +669 -0
  69. pygeodesy/geodsolve.py +426 -0
  70. pygeodesy/geohash.py +914 -0
  71. pygeodesy/geoids.py +1884 -0
  72. pygeodesy/hausdorff.py +892 -0
  73. pygeodesy/heights.py +1155 -0
  74. pygeodesy/interns.py +687 -0
  75. pygeodesy/iters.py +545 -0
  76. pygeodesy/karney.py +919 -0
  77. pygeodesy/ktm.py +633 -0
  78. pygeodesy/latlonBase.py +1766 -0
  79. pygeodesy/lazily.py +960 -0
  80. pygeodesy/lcc.py +684 -0
  81. pygeodesy/ltp.py +1107 -0
  82. pygeodesy/ltpTuples.py +1563 -0
  83. pygeodesy/mgrs.py +721 -0
  84. pygeodesy/named.py +1324 -0
  85. pygeodesy/namedTuples.py +683 -0
  86. pygeodesy/nvectorBase.py +695 -0
  87. pygeodesy/osgr.py +781 -0
  88. pygeodesy/points.py +1686 -0
  89. pygeodesy/props.py +628 -0
  90. pygeodesy/resections.py +1048 -0
  91. pygeodesy/rhumb/__init__.py +46 -0
  92. pygeodesy/rhumb/aux_.py +397 -0
  93. pygeodesy/rhumb/bases.py +1148 -0
  94. pygeodesy/rhumb/ekx.py +563 -0
  95. pygeodesy/rhumb/solve.py +572 -0
  96. pygeodesy/simplify.py +647 -0
  97. pygeodesy/solveBase.py +472 -0
  98. pygeodesy/sphericalBase.py +724 -0
  99. pygeodesy/sphericalNvector.py +1264 -0
  100. pygeodesy/sphericalTrigonometry.py +1447 -0
  101. pygeodesy/streprs.py +627 -0
  102. pygeodesy/trf.py +2079 -0
  103. pygeodesy/triaxials.py +1484 -0
  104. pygeodesy/units.py +969 -0
  105. pygeodesy/unitsBase.py +349 -0
  106. pygeodesy/ups.py +538 -0
  107. pygeodesy/utily.py +1231 -0
  108. pygeodesy/utm.py +762 -0
  109. pygeodesy/utmups.py +318 -0
  110. pygeodesy/utmupsBase.py +517 -0
  111. pygeodesy/vector2d.py +785 -0
  112. pygeodesy/vector3d.py +968 -0
  113. pygeodesy/vector3dBase.py +1049 -0
  114. pygeodesy/webmercator.py +383 -0
  115. pygeodesy/wgrs.py +439 -0
pygeodesy/hausdorff.py ADDED
@@ -0,0 +1,892 @@
1
+
2
+ # -*- coding: utf-8 -*-
3
+
4
+ u'''Hausdorff distances.
5
+
6
+ Classes L{Hausdorff}, L{HausdorffDegrees}, L{HausdorffRadians},
7
+ L{HausdorffCosineAndoyerLambert}, L{HausdorffCosineForsytheAndoyerLambert},
8
+ L{HausdorffCosineLaw}, L{HausdorffDistanceTo}, L{HausdorffEquirectangular},
9
+ L{HausdorffEuclidean}, L{HausdorffFlatLocal}, L{HausdorffFlatPolar},
10
+ L{HausdorffHaversine}, L{HausdorffHubeny}, L{HausdorffKarney},
11
+ L{HausdorffThomas} and L{HausdorffVincentys} to compute U{Hausdorff
12
+ <https://WikiPedia.org/wiki/Hausdorff_distance>} distances between two
13
+ sets of C{LatLon}, C{NumPy}, C{tuples} or other types of points.
14
+
15
+ Only L{HausdorffDistanceTo} -iff used with L{ellipsoidalKarney.LatLon}
16
+ points- and L{HausdorffKarney} requires installation of I{Charles Karney}'s
17
+ U{geographiclib<https://PyPI.org/project/geographiclib>}.
18
+
19
+ Typical usage is as follows. First, create a C{Hausdorff} calculator
20
+ from a given set of C{LatLon} points, called the C{model} or C{template}
21
+ points.
22
+
23
+ C{h = HausdorffXyz(point1s, ...)}
24
+
25
+ Get the C{directed} or C{symmetric} Hausdorff distance to a second set
26
+ of C{LatLon} points, named the C{target} points, by using
27
+
28
+ C{t6 = h.directed(point2s)}
29
+
30
+ respectively
31
+
32
+ C{t6 = h.symmetric(point2s)}.
33
+
34
+ Or, use function C{hausdorff_} with a proper C{distance} function and
35
+ optionally a C{point} function passed as keyword arguments as follows
36
+
37
+ C{t6 = hausdorff_(point1s, point2s, ..., distance=..., point=...)}.
38
+
39
+ In all cases, the returned result C{t6} is a L{Hausdorff6Tuple}.
40
+
41
+ For C{(lat, lon, ...)} points in a C{NumPy} array or plain C{tuples},
42
+ wrap the points in a L{Numpy2LatLon} respectively L{Tuple2LatLon}
43
+ instance, more details in the documentation thereof.
44
+
45
+ For other points, create a L{Hausdorff} sub-class with the appropriate
46
+ C{distance} method overloading L{Hausdorff.distance} and optionally a
47
+ C{point} method overriding L{Hausdorff.point} as the next example.
48
+
49
+ >>> from pygeodesy import Hausdorff, hypot_
50
+ >>>
51
+ >>> class H3D(Hausdorff):
52
+ >>> """Custom Hausdorff example.
53
+ >>> """
54
+ >>> def distance(self, p1, p2):
55
+ >>> return hypot_(p1.x - p2.x, p1.y - p2.y, p1.z - p2.z)
56
+ >>>
57
+ >>> h3D = H3D(xyz1, ..., units="...")
58
+ >>> d6 = h3D.directed(xyz2)
59
+
60
+ Transcribed from the original SciPy U{Directed Hausdorff Code
61
+ <https://GitHub.com/scipy/scipy/blob/master/scipy/spatial/_hausdorff.pyx>}
62
+ version 0.19.0, Copyright (C) Tyler Reddy, Richard Gowers, and Max Linke,
63
+ 2016, distributed under the same BSD license as SciPy, including C{early
64
+ breaking} and C{random sampling} as in U{Abdel Aziz Taha, Allan Hanbury
65
+ "An Efficient Algorithm for Calculating the Exact Hausdorff Distance"
66
+ <https://Publik.TUWien.ac.AT/files/PubDat_247739.pdf>}, IEEE Trans. Pattern
67
+ Analysis Machine Intelligence (PAMI), vol 37, no 11, pp 2153-2163, Nov 2015.
68
+ '''
69
+
70
+ from pygeodesy.constants import INF, NINF, _0_0
71
+ from pygeodesy.datums import _ellipsoidal_datum, _WGS84
72
+ from pygeodesy.errors import PointsError, _xattr, _xcallable, _xkwds, _xkwds_get
73
+ import pygeodesy.formy as _formy
74
+ from pygeodesy.interns import NN, _i_, _j_, _units_
75
+ # from pygeodesy.iters import points2 # from .points
76
+ from pygeodesy.lazily import _ALL_LAZY, _FOR_DOCS
77
+ from pygeodesy.named import _Named, _NamedTuple, notOverloaded, _Pass
78
+ # from pygeodesy.namedTuples import PhiLam2Tuple # from .points
79
+ from pygeodesy.points import _distanceTo, points2 as _points2, PhiLam2Tuple, radians
80
+ from pygeodesy.props import Property_RO, property_doc_, property_RO
81
+ from pygeodesy.units import Float, Number_, _xUnit, _xUnits
82
+ from pygeodesy.unitsBase import _Str_degrees, _Str_degrees2, _Str_meter, _Str_NN, \
83
+ _Str_radians, _Str_radians2
84
+
85
+ # from math import radians # from .points
86
+ from random import Random
87
+
88
+ __all__ = _ALL_LAZY.hausdorff
89
+ __version__ = '24.03.24'
90
+
91
+
92
+ class HausdorffError(PointsError):
93
+ '''Hausdorff issue.
94
+ '''
95
+ pass
96
+
97
+
98
+ class Hausdorff(_Named):
99
+ '''Hausdorff base class, requires method L{Hausdorff.distance} to
100
+ be overloaded.
101
+ '''
102
+ _datum = _WGS84
103
+ _func = None # formy function
104
+ _kwds = {} # func_ options
105
+ _model = ()
106
+ _seed = None
107
+ _units = _Str_NN # XXX Str to _Pass and for backward compatibility
108
+
109
+ def __init__(self, point1s, seed=None, name=NN, units=NN, **kwds):
110
+ '''New C{Hausdorff...} calculator.
111
+
112
+ @arg point1s: Initial set of points, aka the C{model} or
113
+ C{template} (C{LatLon}[], C{Numpy2LatLon}[],
114
+ C{Tuple2LatLon}[] or C{other}[]).
115
+ @kwarg seed: Random sampling seed (C{any}) or C{None}, C{0}
116
+ or C{False} for no U{random sampling<https://
117
+ Publik.TUWien.ac.AT/files/PubDat_247739.pdf>}.
118
+ @kwarg name: Optional name for this interpolator (C{str}).
119
+ @kwarg units: Optional, the distance units (C{Unit} or C{str}).
120
+ @kwarg kwds: Optional keyword argument for distance function,
121
+ retrievable with property C{kwds}.
122
+
123
+ @raise HausdorffError: Insufficient number of B{C{point1s}}
124
+ or an invalid B{C{point1}}, B{C{seed}}
125
+ or B{C{units}}.
126
+ '''
127
+ _, self._model = self._points2(point1s)
128
+ if seed:
129
+ self.seed = seed
130
+ if name:
131
+ self.name = name
132
+ if units: # and not self.units:
133
+ self.units = units
134
+ if kwds:
135
+ self._kwds = kwds
136
+
137
+ @Property_RO
138
+ def adjust(self):
139
+ '''Get the adjust setting (C{bool} or C{None} if not applicable).
140
+ '''
141
+ return _xkwds_get(self._kwds, adjust=None)
142
+
143
+ @Property_RO
144
+ def datum(self):
145
+ '''Get the datum of this calculator (L{Datum} or C{None} if not applicable).
146
+ '''
147
+ return self._datum
148
+
149
+ def _datum_setter(self, datum):
150
+ '''(INTERNAL) Set the datum.
151
+ '''
152
+ d = datum or _xattr(self._model[0], datum=datum)
153
+ if d not in (None, self._datum): # PYCHOK no cover
154
+ self._datum = _ellipsoidal_datum(d, name=self.name)
155
+
156
+ def directed(self, point2s, early=True):
157
+ '''Compute only the C{forward Hausdorff} distance.
158
+
159
+ @arg point2s: Second set of points, aka the C{target} (C{LatLon}[],
160
+ C{Numpy2LatLon}[], C{Tuple2LatLon}[] or C{other}[]).
161
+ @kwarg early: Enable or disable U{early breaking<https://
162
+ Publik.TUWien.ac.AT/files/PubDat_247739.pdf>} (C{bool}).
163
+
164
+ @return: A L{Hausdorff6Tuple}C{(hd, i, j, mn, md, units)}.
165
+
166
+ @raise HausdorffError: Insufficient number of B{C{point2s}} or
167
+ an invalid B{C{point2}}.
168
+
169
+ @note: See B{C{point2s}} note at L{HausdorffDistanceTo}.
170
+ '''
171
+ return self._hausdorff_(point2s, False, early, self.distance)
172
+
173
+ def distance(self, point1, point2):
174
+ '''Return the distance between B{C{point1}} and B{C{point2s}},
175
+ subject to the supplied optional keyword arguments, see
176
+ property C{kwds}.
177
+ '''
178
+ return self._func(point1.lat, point1.lon,
179
+ point2.lat, point2.lon, **self._kwds)
180
+
181
+ def _hausdorff_(self, point2s, both, early, distance):
182
+ _, ps2 = self._points2(point2s)
183
+ return _hausdorff_(self._model, ps2, both, early, self.seed,
184
+ self.units, distance, self.point)
185
+
186
+ @property_RO
187
+ def kwds(self):
188
+ '''Get the supplied, optional keyword arguments (C{dict}).
189
+ '''
190
+ return self._kwds
191
+
192
+ def point(self, point):
193
+ '''Convert a C{model} or C{target} point for the C{.distance} method.
194
+ '''
195
+ return point # pass thru
196
+
197
+ def _points2(self, points):
198
+ '''(INTERNAL) Check a set of points.
199
+ '''
200
+ return _points2(points, closed=False, Error=HausdorffError)
201
+
202
+ @property_doc_(''' the random sampling seed (C{Random}).''')
203
+ def seed(self):
204
+ '''Get the random sampling seed (C{any} or C{None}).
205
+ '''
206
+ return self._seed
207
+
208
+ @seed.setter # PYCHOK setter!
209
+ def seed(self, seed):
210
+ '''Set the random sampling seed (C{Random(seed)}) or
211
+ C{None}, C{0} or C{False} for no U{random sampling
212
+ <https://Publik.TUWien.ac.AT/files/PubDat_247739.pdf>}.
213
+
214
+ @raise HausdorffError: Invalid B{C{seed}}.
215
+ '''
216
+ if seed:
217
+ try:
218
+ Random(seed)
219
+ except (TypeError, ValueError) as x:
220
+ raise HausdorffError(seed=seed, cause=x)
221
+ self._seed = seed
222
+ else:
223
+ self._seed = None
224
+
225
+ def symmetric(self, point2s, early=True):
226
+ '''Compute the combined C{forward and reverse Hausdorff} distance.
227
+
228
+ @arg point2s: Second set of points, aka the C{target} (C{LatLon}[],
229
+ C{Numpy2LatLon}[], C{Tuple2LatLon}[] or C{other}[]).
230
+ @kwarg early: Enable or disable U{early breaking<https://
231
+ Publik.TUWien.ac.AT/files/PubDat_247739.pdf>} (C{bool}).
232
+
233
+ @return: A L{Hausdorff6Tuple}C{(hd, i, j, mn, md, units)}.
234
+
235
+ @raise HausdorffError: Insufficient number of B{C{point2s}} or
236
+ an invalid B{C{point2}}.
237
+
238
+ @note: See B{C{point2s}} note at L{HausdorffDistanceTo}.
239
+ '''
240
+ return self._hausdorff_(point2s, True, early, self.distance)
241
+
242
+ @property_doc_(''' the distance units (C{Unit} or C{str}).''')
243
+ def units(self):
244
+ '''Get the distance units (C{Unit} or C{str}).
245
+ '''
246
+ return self._units
247
+
248
+ @units.setter # PYCHOK setter!
249
+ def units(self, units):
250
+ '''Set the distance units (C{Unit} or C{str}).
251
+
252
+ @raise TypeError: Invalid B{C{units}}.
253
+ '''
254
+ self._units = _xUnits(units, Base=Float)
255
+
256
+ @Property_RO
257
+ def wrap(self):
258
+ '''Get the wrap setting (C{bool} or C{None} if not applicable).
259
+ '''
260
+ return _xkwds_get(self._kwds, adjust=None)
261
+
262
+
263
+ class HausdorffDegrees(Hausdorff):
264
+ '''L{Hausdorff} base class for distances from C{LatLon}
265
+ points in C{degrees}.
266
+ '''
267
+ _units = _Str_degrees
268
+
269
+ if _FOR_DOCS:
270
+ __init__ = Hausdorff.__init__
271
+ directed = Hausdorff.directed
272
+ symmetric = Hausdorff.symmetric
273
+
274
+ def distance(self, point1, point2): # PYCHOK no cover
275
+ '''I{Must be overloaded}.'''
276
+ notOverloaded(self, point1, point2)
277
+
278
+
279
+ class HausdorffRadians(Hausdorff):
280
+ '''L{Hausdorff} base class for distances from C{LatLon}
281
+ points converted from C{degrees} to C{radians}.
282
+ '''
283
+ _units = _Str_radians
284
+
285
+ if _FOR_DOCS:
286
+ __init__ = Hausdorff.__init__
287
+ directed = Hausdorff.directed
288
+ symmetric = Hausdorff.symmetric
289
+
290
+ def distance(self, point1, point2): # PYCHOK no cover
291
+ '''Return the distance in C{radians} between B{C{point1}} and B{C{point2}}.
292
+ I{Must be overloaded}.'''
293
+ notOverloaded(self, point1, point2)
294
+
295
+ def point(self, point):
296
+ '''Return B{C{point}} as L{PhiLam2Tuple} to maintain
297
+ I{backward compatibility} of L{HausdorffRadians}.
298
+
299
+ @return: A L{PhiLam2Tuple}C{(phi, lam)}.
300
+ '''
301
+ try:
302
+ return point.philam
303
+ except AttributeError:
304
+ return PhiLam2Tuple(radians(point.lat), radians(point.lon))
305
+
306
+
307
+ class _HausdorffMeterRadians(Hausdorff):
308
+ '''(INTERNAL) Returning C{meter} or C{radians} depending on
309
+ the optional keyword arguments supplied at instantiation
310
+ of the C{Hausdorff*} sub-class.
311
+ '''
312
+ _units = _Str_meter
313
+ _units_ = _Str_radians
314
+
315
+ def directed(self, point2s, early=True):
316
+ '''Overloaded method L{Hausdorff.directed} to determine
317
+ the distance function and units from the optional
318
+ keyword arguments given at this instantiation, see
319
+ property C{kwds}.
320
+
321
+ @see: L{Hausdorff.directed} for other details.
322
+ '''
323
+ return self._hausdorff_(point2s, False, early, _formy._radistance(self))
324
+
325
+ def symmetric(self, point2s, early=True):
326
+ '''Overloaded method L{Hausdorff.symmetric} to determine
327
+ the distance function and units from the optional
328
+ keyword arguments given at this instantiation, see
329
+ property C{kwds}.
330
+
331
+ @see: L{Hausdorff.symmetric} for other details.
332
+ '''
333
+ return self._hausdorff_(point2s, True, early, _formy._radistance(self))
334
+
335
+ def _func_(self, *args, **kwds): # PYCHOK no cover
336
+ '''(INTERNAL) I{Must be overloaded}.'''
337
+ notOverloaded(self, *args, **kwds)
338
+
339
+
340
+ class HausdorffCosineAndoyerLambert(_HausdorffMeterRadians):
341
+ '''Compute the C{Hausdorff} distance based on the I{angular} distance
342
+ in C{radians} from function L{pygeodesy.cosineAndoyerLambert}.
343
+ '''
344
+ def __init__(self, point1s, seed=None, name=NN, **datum_wrap):
345
+ '''New L{HausdorffCosineAndoyerLambert} calculator.
346
+
347
+ @see: L{Hausdorff.__init__} for details about B{C{point1s}},
348
+ B{C{seed}}, B{C{name}} and other exceptions.
349
+
350
+ @kwarg datum_wrap: Optional keyword arguments for function
351
+ L{pygeodesy.cosineAndoyerLambert}.
352
+ '''
353
+ Hausdorff.__init__(self, point1s, seed=seed, name=name,
354
+ **datum_wrap)
355
+ self._func = _formy.cosineAndoyerLambert
356
+ self._func_ = _formy.cosineAndoyerLambert_
357
+
358
+ if _FOR_DOCS:
359
+ directed = Hausdorff.directed
360
+ symmetric = Hausdorff.symmetric
361
+
362
+
363
+ class HausdorffCosineForsytheAndoyerLambert(_HausdorffMeterRadians):
364
+ '''Compute the C{Hausdorff} distance based on the I{angular} distance
365
+ in C{radians} from function L{pygeodesy.cosineForsytheAndoyerLambert}.
366
+ '''
367
+ def __init__(self, point1s, seed=None, name=NN, **datum_wrap):
368
+ '''New L{HausdorffCosineForsytheAndoyerLambert} calculator.
369
+
370
+ @see: L{Hausdorff.__init__} for details about B{C{point1s}},
371
+ B{C{seed}}, B{C{name}} and other exceptions.
372
+
373
+ @kwarg datum_wrap: Optional keyword arguments for function
374
+ L{pygeodesy.cosineAndoyerLambert}.
375
+ '''
376
+ Hausdorff.__init__(self, point1s, seed=seed, name=name,
377
+ **datum_wrap)
378
+ self._func = _formy.cosineForsytheAndoyerLambert
379
+ self._func_ = _formy.cosineForsytheAndoyerLambert_
380
+
381
+ if _FOR_DOCS:
382
+ directed = Hausdorff.directed
383
+ symmetric = Hausdorff.symmetric
384
+
385
+
386
+ class HausdorffCosineLaw(_HausdorffMeterRadians):
387
+ '''Compute the C{Hausdorff} distance based on the I{angular}
388
+ distance in C{radians} from function L{pygeodesy.cosineLaw_}.
389
+
390
+ @note: See note at function L{pygeodesy.vincentys_}.
391
+ '''
392
+ def __init__(self, point1s, seed=None, name=NN, **radius_wrap):
393
+ '''New L{HausdorffCosineLaw} calculator.
394
+
395
+ @kwarg radius_wrap: Optional keyword arguments for function
396
+ L{pygeodesy.cosineLaw}.
397
+
398
+ @see: L{Hausdorff.__init__} for details about B{C{point1s}},
399
+ B{C{seed}}, B{C{name}} and other exceptions.
400
+ '''
401
+ Hausdorff.__init__(self, point1s, seed=seed, name=name,
402
+ **radius_wrap)
403
+ self._func = _formy.cosineLaw
404
+ self._func_ = _formy.cosineLaw_
405
+
406
+ if _FOR_DOCS:
407
+ directed = Hausdorff.directed
408
+ symmetric = Hausdorff.symmetric
409
+
410
+
411
+ class HausdorffDistanceTo(Hausdorff):
412
+ '''Compute the C{Hausdorff} distance based on the distance from the
413
+ points' C{LatLon.distanceTo} method, conventionally in C{meter}.
414
+ '''
415
+ _units = _Str_meter
416
+
417
+ def __init__(self, point1s, seed=None, name=NN, **distanceTo_kwds):
418
+ '''New L{HausdorffDistanceTo} calculator.
419
+
420
+ @kwarg distanceTo_kwds: Optional keyword arguments for each
421
+ B{C{point1s}}' C{LatLon.distanceTo}
422
+ method.
423
+
424
+ @see: L{Hausdorff.__init__} for details about B{C{point1s}},
425
+ B{C{seed}}, B{C{name}} and other exceptions.
426
+
427
+ @note: All C{model}, C{template} and C{target} B{C{points}}
428
+ I{must} be instances of the same ellipsoidal or
429
+ spherical C{LatLon} class.
430
+ '''
431
+ Hausdorff.__init__(self, point1s, seed=seed, name=name,
432
+ **distanceTo_kwds)
433
+
434
+ if _FOR_DOCS:
435
+ directed = Hausdorff.directed
436
+ symmetric = Hausdorff.symmetric
437
+
438
+ def distance(self, p1, p2):
439
+ '''Return the distance in C{meter}.
440
+ '''
441
+ return p1.distanceTo(p2, **self._kwds)
442
+
443
+ def _points2(self, points):
444
+ '''(INTERNAL) Check a set of points.
445
+ '''
446
+ np, ps = Hausdorff._points2(self, points)
447
+ return np, _distanceTo(HausdorffError, points=ps)
448
+
449
+
450
+ class HausdorffEquirectangular(Hausdorff):
451
+ '''Compute the C{Hausdorff} distance based on the C{equirectangular} distance
452
+ in C{radians squared} like function L{pygeodesy.equirectangular_}.
453
+ '''
454
+ _units = _Str_degrees2
455
+
456
+ def __init__(self, point1s, seed=None, name=NN, **adjust_limit_wrap):
457
+ '''New L{HausdorffEquirectangular} calculator.
458
+
459
+ @kwarg adjust_limit_wrap: Optional keyword arguments for function
460
+ L{pygeodesy.equirectangular_} I{with default}
461
+ C{B{limit}=0} for I{backward compatibility}.
462
+
463
+ @see: L{Hausdorff.__init__} for details about B{C{point1s}},
464
+ B{C{seed}}, B{C{name}} and other exceptions.
465
+ '''
466
+ adjust_limit_wrap = _xkwds(adjust_limit_wrap, limit=0)
467
+ Hausdorff.__init__(self, point1s, seed=seed, name=name,
468
+ **adjust_limit_wrap)
469
+ self._func = _formy._equirectangular # helper
470
+
471
+ if _FOR_DOCS:
472
+ directed = Hausdorff.directed
473
+ symmetric = Hausdorff.symmetric
474
+
475
+
476
+ class HausdorffEuclidean(_HausdorffMeterRadians):
477
+ '''Compute the C{Hausdorff} distance based on the C{Euclidean}
478
+ distance in C{radians} from function L{pygeodesy.euclidean_}.
479
+ '''
480
+ def __init__(self, point1s, seed=None, name=NN, **adjust_wrap):
481
+ '''New L{HausdorffEuclidean} calculator.
482
+
483
+ @kwarg adjust_radius_wrap: Optional keyword arguments for
484
+ function L{pygeodesy.euclidean}.
485
+
486
+ @see: L{Hausdorff.__init__} for details about B{C{point1s}},
487
+ B{C{seed}}, B{C{name}} and other exceptions.
488
+ '''
489
+ Hausdorff.__init__(self, point1s, seed=seed, name=name,
490
+ **adjust_wrap)
491
+ self._func = _formy.euclidean
492
+ self._func_ = _formy.euclidean_
493
+
494
+ if _FOR_DOCS:
495
+ directed = Hausdorff.directed
496
+ symmetric = Hausdorff.symmetric
497
+
498
+
499
+ class HausdorffExact(Hausdorff):
500
+ '''Compute the C{Hausdorff} distance based on the I{angular}
501
+ distance in C{degrees} from method L{GeodesicExact}C{.Inverse}.
502
+ '''
503
+ _units = _Str_degrees
504
+
505
+ def __init__(self, point1s, seed=None, name=NN, datum=None, **wrap):
506
+ '''New L{HausdorffKarney} calculator.
507
+
508
+ @kwarg datum: Datum to override the default C{Datums.WGS84} and
509
+ first B{C{point1s}}' datum (L{Datum}, L{Ellipsoid},
510
+ L{Ellipsoid2} or L{a_f2Tuple}).
511
+ @kwarg wrap: Optional keyword argument for method C{Inverse1}
512
+ of class L{geodesicx.GeodesicExact}.
513
+
514
+ @see: L{Hausdorff.__init__} for details about B{C{point1s}},
515
+ B{C{seed}}, B{C{name}} and other exceptions.
516
+
517
+ @raise TypeError: Invalid B{C{datum}}.
518
+ '''
519
+ Hausdorff.__init__(self, point1s, seed=seed, name=name,
520
+ **wrap)
521
+ self._datum_setter(datum)
522
+ self._func = self.datum.ellipsoid.geodesicx.Inverse1 # note -x
523
+
524
+ if _FOR_DOCS:
525
+ directed = Hausdorff.directed
526
+ symmetric = Hausdorff.symmetric
527
+
528
+
529
+ class HausdorffFlatLocal(_HausdorffMeterRadians):
530
+ '''Compute the C{Hausdorff} distance based on the I{angular} distance in
531
+ C{radians squared} like function L{pygeodesy.flatLocal_}/L{pygeodesy.hubeny_}.
532
+ '''
533
+ _units = _Str_radians2
534
+
535
+ def __init__(self, point1s, seed=None, name=NN, **datum_scaled_wrap):
536
+ '''New L{HausdorffFlatLocal}/L{HausdorffHubeny} calculator.
537
+
538
+ @kwarg datum_scaled_wrap: Optional keyword arguments for
539
+ function L{pygeodesy.flatLocal}.
540
+
541
+ @see: L{Hausdorff.__init__} for details about B{C{point1s}},
542
+ B{C{seed}}, B{C{name}} and other exceptions.
543
+
544
+ @note: The distance C{units} are C{radians squared}, not C{radians}.
545
+ '''
546
+ Hausdorff.__init__(self, point1s, seed=seed, name=name,
547
+ **datum_scaled_wrap)
548
+ self._func = _formy.flatLocal
549
+ self._func_ = self.datum.ellipsoid._hubeny_2
550
+
551
+ if _FOR_DOCS:
552
+ directed = Hausdorff.directed
553
+ symmetric = Hausdorff.symmetric
554
+
555
+
556
+ class HausdorffFlatPolar(_HausdorffMeterRadians):
557
+ '''Compute the C{Hausdorff} distance based on the I{angular}
558
+ distance in C{radians} from function L{pygeodesy.flatPolar_}.
559
+ '''
560
+ _wrap = False
561
+
562
+ def __init__(self, points, seed=None, name=NN, **radius_wrap):
563
+ '''New L{HausdorffFlatPolar} calculator.
564
+
565
+ @kwarg radius_wrap: Optional keyword arguments for function
566
+ L{pygeodesy.flatPolar}.
567
+
568
+ @see: L{Hausdorff.__init__} for details about B{C{point1s}},
569
+ B{C{seed}}, B{C{name}} and other exceptions.
570
+ '''
571
+ Hausdorff.__init__(self, points, seed=seed, name=name,
572
+ **radius_wrap)
573
+ self._func = _formy.flatPolar
574
+ self._func_ = _formy.flatPolar_
575
+
576
+ if _FOR_DOCS:
577
+ directed = Hausdorff.directed
578
+ symmetric = Hausdorff.symmetric
579
+
580
+
581
+ class HausdorffHaversine(_HausdorffMeterRadians):
582
+ '''Compute the C{Hausdorff} distance based on the I{angular}
583
+ distance in C{radians} from function L{pygeodesy.haversine_}.
584
+
585
+ @note: See note under L{HausdorffVincentys}.
586
+ '''
587
+ _wrap = False
588
+
589
+ def __init__(self, points, seed=None, name=NN, **radius_wrap):
590
+ '''New L{HausdorffHaversine} calculator.
591
+
592
+ @kwarg radius_wrap: Optional keyword arguments for function
593
+ L{pygeodesy.haversine}.
594
+
595
+ @see: L{Hausdorff.__init__} for details about B{C{point1s}},
596
+ B{C{seed}}, B{C{name}} and other exceptions.
597
+ '''
598
+ Hausdorff.__init__(self, points, seed=seed, name=name,
599
+ **radius_wrap)
600
+ self._func = _formy.haversine
601
+ self._func_ = _formy.haversine_
602
+
603
+ if _FOR_DOCS:
604
+ directed = Hausdorff.directed
605
+ symmetric = Hausdorff.symmetric
606
+
607
+
608
+ class HausdorffHubeny(HausdorffFlatLocal): # for Karl Hubeny
609
+ if _FOR_DOCS:
610
+ __doc__ = HausdorffFlatLocal.__doc__
611
+ __init__ = HausdorffFlatLocal.__init__
612
+ directed = HausdorffFlatLocal.directed
613
+ distance = HausdorffFlatLocal.distance
614
+ symmetric = HausdorffFlatLocal.symmetric
615
+
616
+
617
+ class HausdorffKarney(Hausdorff):
618
+ '''Compute the C{Hausdorff} distance based on the I{angular}
619
+ distance in C{degrees} from I{Karney}'s U{geographiclib
620
+ <https://PyPI.org/project/geographiclib>} U{Geodesic
621
+ <https://GeographicLib.SourceForge.io/Python/doc/code.html>}
622
+ Inverse method.
623
+ '''
624
+ _units = _Str_degrees
625
+
626
+ def __init__(self, point1s, datum=None, seed=None, name=NN, **wrap):
627
+ '''New L{HausdorffKarney} calculator.
628
+
629
+ @kwarg datum: Datum to override the default C{Datums.WGS84} and
630
+ first B{C{knots}}' datum (L{Datum}, L{Ellipsoid},
631
+ L{Ellipsoid2} or L{a_f2Tuple}).
632
+ @kwarg wrap: Optional keyword argument for method C{Inverse1}
633
+ of class L{geodesicw.Geodesic}.
634
+
635
+ @raise ImportError: Package U{geographiclib
636
+ <https://PyPI.org/project/geographiclib>} missing.
637
+
638
+ @raise TypeError: Invalid B{C{datum}}.
639
+
640
+ @see: L{Hausdorff.__init__} for details about B{C{point1s}},
641
+ B{C{seed}}, B{C{name}} and other exceptions.
642
+ '''
643
+ Hausdorff.__init__(self, point1s, seed=seed, name=name,
644
+ **wrap)
645
+ self._datum_setter(datum)
646
+ self._func = self.datum.ellipsoid.geodesic.Inverse1
647
+
648
+
649
+ class HausdorffThomas(_HausdorffMeterRadians):
650
+ '''Compute the C{Hausdorff} distance based on the I{angular}
651
+ distance in C{radians} from function L{pygeodesy.thomas_}.
652
+ '''
653
+ def __init__(self, point1s, seed=None, name=NN, **datum_wrap):
654
+ '''New L{HausdorffThomas} calculator.
655
+
656
+ @kwarg datum_wrap: Optional keyword argument for function
657
+ L{pygeodesy.thomas}.
658
+
659
+ @see: L{Hausdorff.__init__} for details about B{C{point1s}},
660
+ B{C{seed}}, B{C{name}} and other exceptions.
661
+ '''
662
+ Hausdorff.__init__(self, point1s, seed=seed, name=name,
663
+ **datum_wrap)
664
+ self._func = _formy.thomas
665
+ self._func_ = _formy.thomas_
666
+
667
+ if _FOR_DOCS:
668
+ directed = Hausdorff.directed
669
+ symmetric = Hausdorff.symmetric
670
+
671
+
672
+ class HausdorffVincentys(_HausdorffMeterRadians):
673
+ '''Compute the C{Hausdorff} distance based on the I{angular}
674
+ distance in C{radians} from function L{pygeodesy.vincentys_}.
675
+
676
+ @note: See note at function L{pygeodesy.vincentys_}.
677
+ '''
678
+ _wrap = False
679
+
680
+ def __init__(self, point1s, seed=None, name=NN, **radius_wrap):
681
+ '''New L{HausdorffVincentys} calculator.
682
+
683
+ @kwarg radius_wrap: Optional keyword arguments for function
684
+ L{pygeodesy.vincentys}.
685
+
686
+ @see: L{Hausdorff.__init__} for details about B{C{point1s}},
687
+ B{C{seed}}, B{C{name}} and other exceptions.
688
+ '''
689
+ Hausdorff.__init__(self, point1s, seed=seed, name=name,
690
+ **radius_wrap)
691
+ self._func = _formy.vincentys
692
+ self._func_ = _formy.vincentys_
693
+
694
+ if _FOR_DOCS:
695
+ directed = Hausdorff.directed
696
+ symmetric = Hausdorff.symmetric
697
+
698
+
699
+ def _hausdorff_(ps1, ps2, both, early, seed, units, distance, point):
700
+ '''(INTERNAL) Core of function L{hausdorff_} and methods C{directed}
701
+ and C{symmetric} of classes C{hausdorff.Hausdorff...}.
702
+ '''
703
+ # shuffling the points generally increases the
704
+ # chance of an early break in the inner j loop
705
+ rr = randomrangenerator(seed) if seed else range
706
+
707
+ hd = NINF
708
+ hi = hj = m = mn = 0
709
+ md = _0_0
710
+
711
+ # forward or forward and backward
712
+ for fb in range(2 if both else 1):
713
+ n = len(ps2)
714
+ for i in rr(len(ps1)):
715
+ p1 = point(ps1[i])
716
+ dh, dj = INF, 0
717
+ for j in rr(n):
718
+ p2 = point(ps2[j])
719
+ d = distance(p1, p2)
720
+ if early and d < hd:
721
+ break # early
722
+ elif d < dh:
723
+ dh, dj = d, j
724
+ else: # no early break
725
+ if hd < dh:
726
+ hd = dh
727
+ if fb:
728
+ hi, hj = dj, i
729
+ else:
730
+ hi, hj = i, dj
731
+ md += dh
732
+ mn += 1
733
+ m += 1
734
+ # swap model and target
735
+ ps1, ps2 = ps2, ps1
736
+
737
+ md = None if mn < m else (md / float(m))
738
+ return Hausdorff6Tuple(hd, hi, hj, m, md, units)
739
+
740
+
741
+ def _point(p):
742
+ '''Default B{C{point}} callable for function L{hausdorff_}.
743
+
744
+ @arg p: The original C{model} or C{target} point (C{any}).
745
+
746
+ @return: The point, suitable for the L{hausdorff_}
747
+ B{C{distance}} callable.
748
+ '''
749
+ return p
750
+
751
+
752
+ def hausdorff_(model, target, both=False, early=True, seed=None, units=NN,
753
+ distance=None, point=_point):
754
+ '''Compute the C{directed} or C{symmetric} U{Hausdorff
755
+ <https://WikiPedia.org/wiki/Hausdorff_distance>} distance between 2 sets of points
756
+ with or without U{early breaking<https://Publik.TUWien.ac.AT/files/PubDat_247739.pdf>}
757
+ and U{random sampling<https://Publik.TUWien.ac.AT/files/PubDat_247739.pdf>}.
758
+
759
+ @arg model: First set of points (C{LatLon}[], C{Numpy2LatLon}[],
760
+ C{Tuple2LatLon}[] or C{other}[]).
761
+ @arg target: Second set of points (C{LatLon}[], C{Numpy2LatLon}[],
762
+ C{Tuple2LatLon}[] or C{other}[]).
763
+ @kwarg both: Return the C{directed} (forward only) or the C{symmetric}
764
+ (combined forward and reverse) C{Hausdorff} distance (C{bool}).
765
+ @kwarg early: Enable or disable U{early breaking<https://Publik.TUWien.ac.AT/
766
+ files/PubDat_247739.pdf>} (C{bool}).
767
+ @kwarg seed: Random sampling seed (C{any}) or C{None}, C{0} or C{False} for no
768
+ U{random sampling<https://Publik.TUWien.ac.AT/files/PubDat_247739.pdf>}.
769
+ @kwarg units: Optional, the distance units (C{Unit} or C{str}).
770
+ @kwarg distance: Callable returning the distance between a B{C{model}}
771
+ and B{C{target}} point (signature C{(point1, point2)}).
772
+ @kwarg point: Callable returning the B{C{model}} or B{C{target}} point
773
+ suitable for B{C{distance}} (signature C{(point)}).
774
+
775
+ @return: A L{Hausdorff6Tuple}C{(hd, i, j, mn, md, units)}.
776
+
777
+ @raise HausdorffError: Insufficient number of B{C{model}} or B{C{target}} points.
778
+
779
+ @raise TypeError: If B{C{distance}} or B{C{point}} is not callable.
780
+ '''
781
+ _xcallable(distance=distance, point=point)
782
+
783
+ _, ps1 = _points2(model, closed=False, Error=HausdorffError) # PYCHOK non-sequence
784
+ _, ps2 = _points2(target, closed=False, Error=HausdorffError) # PYCHOK non-sequence
785
+ return _hausdorff_(ps1, ps2, both, early, seed, units, distance, point)
786
+
787
+
788
+ class Hausdorff6Tuple(_NamedTuple):
789
+ '''6-Tuple C{(hd, i, j, mn, md, units)} with the U{Hausdorff
790
+ <https://WikiPedia.org/wiki/Hausdorff_distance>} distance C{hd},
791
+ indices C{i} and C{j}, the total count C{mn}, the C{I{mean}
792
+ Hausdorff} distance C{md} and the class or name of both distance
793
+ C{units}.
794
+
795
+ For C{directed Hausdorff} distances, count C{mn} is the number
796
+ of model points considered. For C{symmetric Hausdorff} distances
797
+ count C{mn} twice that.
798
+
799
+ Indices C{i} and C{j} are the C{model} respectively C{target}
800
+ point with the C{hd} distance.
801
+
802
+ Mean distance C{md} is C{None} if an C{early break} occurred and
803
+ U{early breaking<https://Publik.TUWien.ac.AT/files/PubDat_247739.pdf>}
804
+ was enabled by keyword argument C{early=True}.
805
+ '''
806
+ _Names_ = ('hd', _i_, _j_, 'mn', 'md', _units_)
807
+ _Units_ = (_Pass, Number_, Number_, Number_, _Pass, _Pass)
808
+
809
+ def toUnits(self, **Error): # PYCHOK expected
810
+ '''Overloaded C{_NamedTuple.toUnits} for C{hd} and C{md} units.
811
+ '''
812
+ U = _xUnit(self.units, Float) # PYCHOK expected
813
+ M = _Pass if self.md is None else U # PYCHOK expected
814
+ self._Units_ = (U,) + Hausdorff6Tuple._Units_[1:4] \
815
+ + (M,) + Hausdorff6Tuple._Units_[5:]
816
+ return _NamedTuple.toUnits(self, **Error)
817
+
818
+
819
+ def randomrangenerator(seed):
820
+ '''Return a C{seed}ed random range function generator.
821
+
822
+ @arg seed: Initial, internal L{Random} state (C{hashable}
823
+ or C{None}).
824
+
825
+ @note: L{Random} with C{B{seed} is None} seeds from the
826
+ current time or from a platform-specific randomness
827
+ source, if available.
828
+
829
+ @return: A function to generate random ranges.
830
+
831
+ @example:
832
+
833
+ >>> rrange = randomrangenerator('R')
834
+ >>> for r in rrange(n):
835
+ >>> ... # r is random in 0..n-1
836
+ '''
837
+ R = Random(seed)
838
+
839
+ def _range(n, *stop_step):
840
+ '''Like standard L{range}C{start, stop=..., step=...)},
841
+ except the returned values are in random order.
842
+
843
+ @note: Especially C{range(n)} behaves like standard
844
+ L{Random.sample}C{(range(n), n)} but avoids
845
+ creating a tuple with the entire C{population}
846
+ and a list containing all sample values (for
847
+ large C{n}).
848
+ '''
849
+ if stop_step:
850
+ s = range(n, *stop_step)
851
+
852
+ elif n > 32:
853
+ r = R.randrange # Random._randbelow
854
+ s = set()
855
+ for _ in range(n - 32):
856
+ i = r(n)
857
+ while i in s:
858
+ i = r(n)
859
+ s.add(i)
860
+ yield i
861
+ s = set(range(n)) - s # [i for i in range(n) if i not in s]
862
+ else:
863
+ s = range(n)
864
+
865
+ s = list(s)
866
+ R.shuffle(s)
867
+ while s:
868
+ yield s.pop(0)
869
+
870
+ return _range
871
+
872
+ # **) MIT License
873
+ #
874
+ # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
875
+ #
876
+ # Permission is hereby granted, free of charge, to any person obtaining a
877
+ # copy of this software and associated documentation files (the "Software"),
878
+ # to deal in the Software without restriction, including without limitation
879
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
880
+ # and/or sell copies of the Software, and to permit persons to whom the
881
+ # Software is furnished to do so, subject to the following conditions:
882
+ #
883
+ # The above copyright notice and this permission notice shall be included
884
+ # in all copies or substantial portions of the Software.
885
+ #
886
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
887
+ # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
888
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
889
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
890
+ # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
891
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
892
+ # OTHER DEALINGS IN THE SOFTWARE.