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/simplify.py ADDED
@@ -0,0 +1,647 @@
1
+
2
+ # -*- coding: utf-8 -*-
3
+
4
+ u'''Simplify or linearize a path.
5
+
6
+ Each of the I{simplify} functions is based on a different algorithm and
7
+ produces different, simplified results in (very) different run times for
8
+ the same path of C{LatLon} points.
9
+
10
+ Function L{simplify1} eliminates points based on edge lengths shorter
11
+ than a given tolerance.
12
+
13
+ The functions L{simplifyRDP} and L{simplifyRDPm} use the original,
14
+ respectively modified I{Ramer-Douglas-Peucker} (RDP) algorithm, iteratively
15
+ finding the point farthest from each path edge. The difference is that
16
+ function L{simplifyRDP} exhaustively searches the most distant point in
17
+ each iteration, while modified L{simplifyRDPm} stops at the first point
18
+ exceeding the distance tolerance.
19
+
20
+ Function L{simplifyRW} use the I{Reumann-Witkam} method, sliding a "pipe"
21
+ over each path edge, removing all subsequent points within, closer than
22
+ the pipe radius up to the first point outside the pipe.
23
+
24
+ Functions L{simplifyVW} and L{simplifyVWm} are based on the original,
25
+ respectively modified I{Visvalingam-Whyatt} (VW) method using the area of
26
+ the triangle formed by three neigboring points. Function L{simplifyVW}
27
+ removes only a single point per iteration, while modified L{simplifyVWm}
28
+ eliminates in each iteration all points with a triangular area not
29
+ exceeding the tolerance.
30
+
31
+ Functions L{simplifyRDP}, L{simplifyRDPm} and L{simplifyRW} provide
32
+ keyword argument I{shortest} to specify of the distance between a point
33
+ and a path edge. If C{True}, use the I{shortest} distance to the path
34
+ edge or edge points, otherwise use the I{perpendicular} distance to
35
+ the extended line through both path edge points.
36
+
37
+ Keyword argument B{C{radius}} of all fuctions is set to the mean earth
38
+ radius in C{meter}. Other units can be used, provided that the radius
39
+ and tolerance are always specified in the same units.
40
+
41
+ Use keyword argument C{B{indices}=True} in any function to return a
42
+ list of simplified point I{indices} instead of the simplified points.
43
+ The first and last index are always the first and last original index.
44
+
45
+ Finally, any additional keyword arguments B{C{options}} to all functions
46
+ are passed thru to function L{pygeodesy.equirectangular_} to specify the
47
+ distance approximation.
48
+
49
+ To process C{NumPy} arrays containing rows of lat-, longitude and
50
+ possibly other values, use class L{Numpy2LatLon} to wrap the C{NumPy}
51
+ array into I{on-the-fly-LatLon} points. Pass the L{Numpy2LatLon}
52
+ instance to any I{simplify} function and the returned result will be
53
+ a C{NumPy} array containing the simplified subset, a partial copy of
54
+ the original C{NumPy} array. Use keyword argument C{B{indices}=True}
55
+ to return a list of array row indices inlieu of the simplified array
56
+ subset.
57
+
58
+ See:
59
+ - U{https://Bost.Ocks.org/mike/simplify}
60
+ - U{https://WikiPedia.org/wiki/Ramer-Douglas-Peucker_algorithm}
61
+ - U{https://www.ScienceDirect.com/science/article/pii/S0098300402000092}
62
+ - U{https://hydra.Hull.ac.UK/resources/hull:8338}
63
+ - U{https://psimpl.SourceForge.net/reumann-witkam.html}
64
+ - U{https://www.CS.UBC.Ca/cgi-bin/tr/1992/TR-92-07.pdf}
65
+ - U{https://GitHub.com/FlorianWilhelm/gps_data_with_python}
66
+ - U{https://www.BDCC.co.UK/Gmaps/GDouglasPeuker.js}
67
+ - U{https://GitHub.com/mourner/simplify-js}
68
+ - U{https://GitHub.com/OmarEstrella/simplify.py}
69
+ - U{https://PyPI.org/project/rdp}
70
+ - U{https://PyPI.org/project/visvalingam}
71
+ - U{https://PyPI.org/project/simplification}
72
+ '''
73
+ # make sure int/int division yields float quotient, see .basics
74
+ from __future__ import division as _; del _ # PYCHOK semicolon
75
+
76
+ # from pygeodesy.basics import len2 # from .fmath
77
+ from pygeodesy.constants import EPS, R_M, _1_0
78
+ from pygeodesy.errors import _AttributeError, _ValueError
79
+ from pygeodesy.fmath import len2, sqrt0
80
+ from pygeodesy.formy import equirectangular_
81
+ from pygeodesy.interns import _small_, _too_
82
+ from pygeodesy.iters import isNumpy2, isTuple2
83
+ # from pygeodesy.lazily import _ALL_LAZY # from .units
84
+ from pygeodesy.units import _ALL_LAZY, _1mm, Radius_
85
+
86
+ from math import degrees, fabs, radians
87
+
88
+ __all__ = _ALL_LAZY.simplify
89
+ __version__ = '23.03.22'
90
+
91
+
92
+ # try:
93
+ # from collections import namedtuple
94
+ # _T2 = namedtuple('_T2', 'ix, h2')
95
+ # except ImportError:
96
+ # class _T2(object):
97
+ # ...
98
+ # namedtuple (and .named._NamedTuple) can not be
99
+ # used because (a) values can not be updated and
100
+ # (b) it produces PyChecker warning "<string>:28:
101
+ # self is not first method argument" which can't
102
+ # be suppressed with command line option --stdlib
103
+ class _T2(object):
104
+ '''(INTERNAL) VW 2-tuple (index, area).
105
+ '''
106
+ # __slots__ are no longer space savers, see
107
+ # the comments at the class .points.LatLon_
108
+ # __slots__ = ('ix', 'h2')
109
+
110
+ def __init__(self, ix, h2):
111
+ self.ix = ix
112
+ self.h2 = h2
113
+
114
+
115
+ class _Sy(object):
116
+ '''(INTERNAL) Simplify state.
117
+ '''
118
+ d2i2 = None # d2iP2 or d2iS2
119
+ d2yxse5 = () # 5-tuple
120
+ eps = EPS # system epsilon
121
+ indices = False
122
+ n = 0
123
+ options = {}
124
+ pts = []
125
+ radius = R_M # mean earth radius
126
+ r = {} # RDP indices or VW 2-tuples
127
+ s2 = EPS # tolerance squared
128
+ s2e = EPS # sentinel
129
+ subset = None # isNumpy2 or isTuple2
130
+
131
+ def __init__(self, points, tolerance, radius, shortest,
132
+ indices, **options):
133
+ '''New C{Simplify} state.
134
+ '''
135
+ n, self.pts = len2(points)
136
+ if n > 0:
137
+ self.n = n
138
+ self.r = {0: True, n-1: True} # dict to avoid duplicates
139
+
140
+ if isNumpy2(points) or isTuple2(points): # NOT self.pts
141
+ self.subset = points.subset
142
+
143
+ if indices:
144
+ self.indices = True
145
+
146
+ if radius:
147
+ self.radius = Radius_(radius, low=self.eps)
148
+ elif self.radius < self.eps:
149
+ raise _ValueError(radius=radius, txt=_too_(_small_))
150
+
151
+ if options:
152
+ self.options = options
153
+
154
+ # tolerance converted to degrees squared
155
+ self.s2 = degrees(tolerance / self.radius)**2
156
+ if min(self.s2, tolerance) < self.eps:
157
+ raise _ValueError(tolerance=tolerance, txt=_too_(_small_))
158
+ self.s2e = self.s2 + 1 # sentinel
159
+
160
+ # compute either the shortest or perpendicular distance
161
+ self.d2i2 = self.d2iS2 if shortest else self.d2iP2 # PYCHOK attr
162
+
163
+ def d21(self, s, e):
164
+ '''Set path edge or line thru points[s] to -[e].
165
+ '''
166
+ d21, y21, x21, _ = self.d2yxu4(s, e)
167
+ self.d2yxse5 = d21, y21, x21, s, e
168
+ return d21 > self.eps
169
+
170
+ def d2ih2(self, n, m, brk):
171
+ '''Find the tallest distance among all points[n..m]
172
+ to points[s] to -[e] exceeding the tolerance.
173
+ '''
174
+ _, _, _, s, _ = self.d2yxse5
175
+ eps, _d2yxu4 = self.eps, self.d2yxu4
176
+ t2, t = self.s2, 0 # tallest
177
+ for i in range(n, m):
178
+ d2, _, _, _ = _d2yxu4(s, i)
179
+ if d2 > t2:
180
+ t2, t = d2, i
181
+ if brk and d2 > eps:
182
+ break
183
+ return t2, t
184
+
185
+ def d2iP2(self, n, m, brk):
186
+ '''Find the tallest I{perpendicular} distance among all
187
+ points[n..m] to the path edge or line thru points[s]
188
+ to -[e] exceeding the tolerance.
189
+ '''
190
+ d21, y21, x21, s, _ = self.d2yxse5
191
+ eps, _d2yxu4 = self.eps, self.d2yxu4
192
+ t2, t = self.s2, 0 # tallest
193
+ for i in range(n, m):
194
+ d2, y01, x01, _ = _d2yxu4(s, i)
195
+ if d2 > eps:
196
+ # perpendicular distance squared
197
+ d2 = (y01 * x21 - x01 * y21)**2 / d21
198
+ if d2 > t2:
199
+ t2, t = d2, i
200
+ if brk:
201
+ break
202
+ return t2, t
203
+
204
+ def d2iS2(self, n, m, brk):
205
+ '''Find the tallest I{shortest} distance among all
206
+ points[n..m] to the path edge or line thru points[s]
207
+ to -[e] exceeding the tolerance.
208
+ '''
209
+ # point (x, y) on axis rotated by angle a ccw:
210
+ # x' = y * sin(a) + x * cos(a)
211
+ # y' = y * cos(a) - x * sin(a)
212
+ #
213
+ # distance (w) along and perpendicular (h) to
214
+ # a line thru point (dx, dy) and the origin:
215
+ # w = (y * dy + x * dx) / hypot(dx, dy)
216
+ # h = (y * dx - x * dy) / hypot(dx, dy)
217
+
218
+ d21, y21, x21, s, e = self.d2yxse5
219
+ eps, _d2yxu4 = self.eps, self.d2yxu4
220
+ t2, t = self.s2, 0 # tallest
221
+ for i in range(n, m):
222
+ # distance points[i] to -[s]
223
+ d2, y01, x01, _ = _d2yxu4(s, i)
224
+ if d2 > eps:
225
+ w = y01 * y21 + x01 * x21
226
+ if w > 0:
227
+ if w < d21:
228
+ # perpendicular distance squared
229
+ d2 = (y01 * x21 - x01 * y21)**2 / d21
230
+ else: # distance points[i] to -[e]
231
+ d2, _, _, _ = _d2yxu4(e, i)
232
+ if d2 > t2:
233
+ t2, t = d2, i
234
+ if brk:
235
+ break
236
+ return t2, t
237
+
238
+ def d2yxu4(self, i, j):
239
+ '''Return distance I{squared}, points[i] to -[j] deltas
240
+ and the (longitudinal) unrollment.
241
+ '''
242
+ p1, p2= self.pts[i], self.pts[j]
243
+ return equirectangular_(p1.lat, p1.lon,
244
+ p2.lat, p2.lon, **self.options)
245
+
246
+ def h2t(self, i1, i0, i2):
247
+ '''Compute the Visvalingam-Whyatt triangular area,
248
+ points[i0] is the top and points[i1] to -[i2]
249
+ form the base of the triangle.
250
+ '''
251
+ d21, y21, x21 , _= self.d2yxu4(i1, i2)
252
+ if d21 > self.eps:
253
+ d01, y01, x01, _ = self.d2yxu4(i1, i0)
254
+ if d01 > self.eps:
255
+ h2 = fabs(y01 * x21 - x01 * y21)
256
+ # triangle height h = h2 / sqrt(d21) and
257
+ # the area = h * sqrt(d21) / 2 == h2 / 2
258
+ return h2 # double triangle area
259
+ return 0
260
+
261
+ def points(self, r):
262
+ '''Return the list of simplified points or indices.
263
+ '''
264
+ r = sorted(r.keys())
265
+ if self.indices:
266
+ return list(r)
267
+ elif self.subset:
268
+ return self.subset(r)
269
+ else:
270
+ return [self.pts[i] for i in r]
271
+
272
+ def rdp(self, modified):
273
+ '''Ramer-Douglas-Peucker (RDP) simplification of a
274
+ path of C{LatLon} points.
275
+
276
+ @arg modified: Use modified RDP (C{bool}).
277
+ '''
278
+ n, r = self.n, self.r
279
+ if n > 1:
280
+ s2, _d21 = self.s2, self.d21
281
+ _d2i2, _d2ih2 = self.d2i2, self.d2ih2
282
+
283
+ se = [(0, n-1)]
284
+ _a = se.append
285
+ _p = se.pop
286
+ while se:
287
+ s, e = _p()
288
+ s1 = s + 1
289
+ if e > s1:
290
+ if _d21(s, e): # points[] to edge [s, e]
291
+ d2, i = _d2i2(s1, e, modified)
292
+ else: # points[] to point [s]
293
+ d2, i = _d2ih2(s1, e, modified)
294
+ if d2 > s2 and i > 0:
295
+ r[i] = True
296
+ _a((i, e))
297
+ if not modified:
298
+ _a((s, i))
299
+ r[s] = True
300
+
301
+ return self.points(r)
302
+
303
+ def rm1(self, m, tol):
304
+ '''Eliminate one Visvalingam-Whyatt point and recomputes
305
+ the trangular area of both neighboring points, but
306
+ removes those too unless the recomputed area exceeds
307
+ the tolerance.
308
+ '''
309
+ _h2t, r = self.h2t, self.r
310
+
311
+ r.pop(m)
312
+ for n in (m, m - 1):
313
+ while 0 < n < (len(r) - 1):
314
+ h2 = _h2t(r[n-1].ix, r[n].ix, r[n+1].ix)
315
+ if h2 > tol:
316
+ r[n].h2 = h2
317
+ break # while
318
+ else:
319
+ r.pop(n)
320
+
321
+ def rm2(self, tol):
322
+ '''Eliminate all Visvalingam-Whyatt points with a
323
+ triangular area not exceeding the tolerance.
324
+ '''
325
+ r, _rm1 = self.r, self.rm1
326
+
327
+ i = len(r) - 1
328
+ while i > 1:
329
+ i -= 1
330
+ if r[i].h2 <= tol:
331
+ _rm1(i, tol)
332
+ i = min(i, len(r) - 1)
333
+
334
+ def vwn(self):
335
+ '''Initialize Visvalingam-Whyatt as list of 2-tuples
336
+ _T2(ix, h2) where ix is the points[] index and h2
337
+ is the triangular area I{(times 2)} of that point.
338
+ '''
339
+ n, _h2t, s2e, T2 = self.n, self.h2t, self.s2e, _T2
340
+
341
+ self.r = r = []
342
+ if n > 2:
343
+ r[:] = [T2(i, _h2t(i-1, i, i+1)) for i in range(1, n-1)]
344
+ if n > 1:
345
+ r.append(T2(n-1, s2e))
346
+ if n > 0:
347
+ r.insert(0, T2(0, s2e))
348
+ return len(r)
349
+
350
+ def vwr(self, attr):
351
+ '''Return the Visvalingam-Whyatt results, optionally
352
+ including the triangular area (in meters) as
353
+ attribute attr to each simplified point.
354
+ '''
355
+ pts, r = self.pts, self.r
356
+
357
+ # double check the minimal triangular area
358
+ assert min(t2.h2 for t2 in r) > self.s2 > 0
359
+
360
+ if attr: # return the trangular area (actually
361
+ # the sqrt of double the triangular area)
362
+ # converted back from degrees to meter
363
+ if isNumpy2(pts):
364
+ raise _AttributeError(attr=attr)
365
+ m = radians(_1_0) * self.radius
366
+ r[0].h2 = r[-1].h2 = 0 # zap sentinels
367
+ for t2 in r: # convert back to meter
368
+ setattr(pts[t2.ix], attr, sqrt0(t2.h2) * m)
369
+
370
+ # double check for duplicates
371
+ n = len(r)
372
+ r = dict((t2.ix, True) for t2 in r)
373
+ assert len(r) == n
374
+ return self.points(r)
375
+
376
+
377
+ def simplify1(points, distance=_1mm, radius=R_M, indices=False, **options):
378
+ '''Basic simplification of a path of C{LatLon} points.
379
+
380
+ Eliminates any points closer together than the given I{distance}
381
+ tolerance.
382
+
383
+ @arg points: Path points (C{LatLon}[]).
384
+ @kwarg distance: Tolerance (C{meter}, same units as B{C{radius}}).
385
+ @kwarg radius: Mean earth radius (C{meter}).
386
+ @kwarg indices: If C{True} return the simplified point indices
387
+ instead of the simplified points (C{bool}).
388
+ @kwarg options: Optional keyword arguments passed thru to
389
+ function L{pygeodesy.equirectangular_}.
390
+
391
+ @return: Simplified points (C{LatLon}[]).
392
+
393
+ @raise LimitError: Lat- and/or longitudinal delta exceeds the B{C{limit}},
394
+ see function L{pygeodesy.equirectangular_}.
395
+
396
+ @raise ValueError: Tolerance B{C{distance}} or B{C{radius}} too small.
397
+ '''
398
+ S = _Sy(points, distance, radius, True, indices, **options)
399
+
400
+ r, n = S.r, S.n
401
+ if n > 1:
402
+ s2, _d2yxu4 = S.s2, S.d2yxu4
403
+
404
+ i = 0
405
+ for j in range(1, n):
406
+ d2, _, _, _= _d2yxu4(i, j)
407
+ if d2 > s2:
408
+ r[j] = True
409
+ i = j
410
+
411
+ return S.points(r)
412
+
413
+
414
+ def simplifyRDP(points, distance=_1mm, radius=R_M, shortest=False,
415
+ indices=False, **options):
416
+ '''I{Ramer-Douglas-Peucker} (RDP) simplification of a path of
417
+ C{LatLon} points.
418
+
419
+ Eliminates any points too close together or closer to an
420
+ edge than the given I{distance} tolerance.
421
+
422
+ This C{RDP} method exhaustively searches for the point with
423
+ the largest distance, resulting in worst-case complexity
424
+ M{O(n**2)} where M{n} is the number of points.
425
+
426
+ @arg points: Path points (C{LatLon}[]).
427
+ @kwarg distance: Tolerance (C{meter}, same units as B{C{radius}}).
428
+ @kwarg radius: Mean earth radius (C{meter}).
429
+ @kwarg shortest: If C{True} use the I{shortest} otherwise the
430
+ I{perpendicular} distance (C{bool}).
431
+ @kwarg indices: If C{True} return the simplified point indices
432
+ instead of the simplified points (C{bool}).
433
+ @kwarg options: Optional keyword arguments passed thru to
434
+ function L{pygeodesy.equirectangular_}.
435
+
436
+ @return: Simplified points (C{LatLon}[]).
437
+
438
+ @raise LimitError: Lat- and/or longitudinal delta exceeds the B{C{limit}},
439
+ see function L{pygeodesy.equirectangular_}.
440
+
441
+ @raise ValueError: Tolerance B{C{distance}} or B{C{radius}} too small.
442
+ '''
443
+ S = _Sy(points, distance, radius, shortest, indices, **options)
444
+
445
+ return S.rdp(False)
446
+
447
+
448
+ def simplifyRDPm(points, distance=_1mm, radius=R_M, shortest=False,
449
+ indices=False, **options):
450
+ '''Modified I{Ramer-Douglas-Peucker} (RDPm) simplification of a
451
+ path of C{LatLon} points.
452
+
453
+ Eliminates any points too close together or closer to an edge
454
+ than the given I{distance} tolerance.
455
+
456
+ This C{RDPm} method stops at the first point farther than the
457
+ given distance tolerance, significantly reducing the run time
458
+ (but producing results different from the original C{RDP} method).
459
+
460
+ @arg points: Path points (C{LatLon}[]).
461
+ @kwarg distance: Tolerance (C{meter}, same units as B{C{radius}}).
462
+ @kwarg radius: Mean earth radius (C{meter}).
463
+ @kwarg shortest: If C{True} use the I{shortest} otherwise the
464
+ I{perpendicular} distance (C{bool}).
465
+ @kwarg indices: If C{True} return the simplified point indices
466
+ instead of the simplified points (C{bool}).
467
+ @kwarg options: Optional keyword arguments passed thru to
468
+ function L{pygeodesy.equirectangular_}.
469
+
470
+ @return: Simplified points (C{LatLon}[]).
471
+
472
+ @raise LimitError: Lat- and/or longitudinal delta exceeds the B{C{limit}},
473
+ see function L{pygeodesy.equirectangular_}.
474
+
475
+ @raise ValueError: Tolerance B{C{distance}} or B{C{radius}} too small.
476
+ '''
477
+ S = _Sy(points, distance, radius, shortest, indices, **options)
478
+
479
+ return S.rdp(True)
480
+
481
+
482
+ def simplifyRW(points, pipe=_1mm, radius=R_M, shortest=False,
483
+ indices=False, **options):
484
+ '''I{Reumann-Witkam} (RW) simplification of a path of C{LatLon}
485
+ points.
486
+
487
+ Eliminates any points too close together or within the given
488
+ I{pipe} tolerance along an edge.
489
+
490
+ @arg points: Path points (C{LatLon}[]).
491
+ @kwarg pipe: Pipe radius, half-width (C{meter}, same units as
492
+ B{C{radius}}).
493
+ @kwarg radius: Mean earth radius (C{meter}).
494
+ @kwarg shortest: If C{True} use the I{shortest} otherwise the
495
+ I{perpendicular} distance (C{bool}).
496
+ @kwarg indices: If C{True} return the simplified point indices
497
+ instead of the simplified points (C{bool}).
498
+ @kwarg options: Optional keyword arguments passed thru to
499
+ function L{pygeodesy.equirectangular_}.
500
+
501
+ @return: Simplified points (C{LatLon}[]).
502
+
503
+ @raise LimitError: Lat- and/or longitudinal delta exceeds the B{C{limit}},
504
+ see function L{pygeodesy.equirectangular_}.
505
+
506
+ @raise ValueError: Tolerance B{C{pipe}} or B{C{radius}} too small.
507
+ '''
508
+ S = _Sy(points, pipe, radius, shortest, indices, **options)
509
+
510
+ n, r = S.n, S.r
511
+ if n > 1:
512
+ s2, _d21, _d2i2 = S.s2, S.d21, S.d2i2
513
+
514
+ s, e = 0, 1
515
+ while s < e < n:
516
+ if _d21(s, e):
517
+ d2, i = _d2i2(e + 1, n, True)
518
+ if d2 > s2 and i > 0:
519
+ r[s] = r[i] = True
520
+ s, e = i, i + 1
521
+ else:
522
+ r[s] = True # r[n-1] = True
523
+ break # while loop
524
+ else: # drop points[e]
525
+ e += 1
526
+
527
+ return S.points(r)
528
+
529
+
530
+ def simplifyVW(points, area=_1mm, radius=R_M, attr=None,
531
+ indices=False, **options):
532
+ '''I{Visvalingam-Whyatt} (VW) simplification of a path of
533
+ C{LatLon} points.
534
+
535
+ Eliminates any points too close together or with a triangular
536
+ area not exceeding the given I{area} tolerance I{squared}.
537
+
538
+ This C{VW} method exhaustively searches for the single point
539
+ with the smallest triangular area, resulting in worst-case
540
+ complexity M{O(n**2)} where M{n} is the number of points.
541
+
542
+ @arg points: Path points (C{LatLon}[]).
543
+ @kwarg area: Tolerance (C{meter}, same units as B{C{radius}}).
544
+ @kwarg radius: Mean earth radius (C{meter}).
545
+ @kwarg attr: Optional, points attribute to save the area value
546
+ (C{str}).
547
+ @kwarg indices: If C{True} return the simplified point indices
548
+ instead of the simplified points (C{bool}).
549
+ @kwarg options: Optional keyword arguments passed thru to
550
+ function L{pygeodesy.equirectangular_}.
551
+
552
+ @return: Simplified points (C{LatLon}[]).
553
+
554
+ @raise AttributeError: If an B{C{attr}} is specified for I{Numpy2}
555
+ B{C{points}}.
556
+
557
+ @raise LimitError: Lat- and/or longitudinal delta exceeds the B{C{limit}},
558
+ see function L{pygeodesy.equirectangular_}.
559
+
560
+ @raise ValueError: Tolerance B{C{area}} or B{C{radius}} too small.
561
+ '''
562
+ S = _Sy(points, area, radius, False, indices, **options)
563
+
564
+ if S.vwn() > 2:
565
+ # remove any points too close or
566
+ # with a zero triangular area
567
+ S.rm2(0)
568
+
569
+ r, s2, s2e = S.r, S.s2, S.s2e
570
+ # keep removing the point with the smallest
571
+ # area until latter exceeds the tolerance
572
+ while len(r) > 2:
573
+ m, m2 = 0, s2e
574
+ for i in range(1, len(r) - 1):
575
+ h2 = r[i].h2
576
+ if h2 < m2:
577
+ m, m2 = i, h2
578
+ if m2 > s2:
579
+ break
580
+ S.rm1(m, 0)
581
+
582
+ return S.vwr(attr)
583
+
584
+
585
+ def simplifyVWm(points, area=_1mm, radius=R_M, attr=None,
586
+ indices=False, **options):
587
+ '''I{Modified Visvalingam-Whyatt} (VWm) simplification of a path
588
+ of C{LatLon} points.
589
+
590
+ Eliminates any points too close together or with a triangular
591
+ area not exceeding the given area tolerance I{squared}.
592
+
593
+ This C{VWm} method removes all points with a triangular area
594
+ below the tolerance in each iteration, significantly reducing
595
+ the run time (but producing results different from the
596
+ original C{VW} method).
597
+
598
+ @arg points: Path points (C{LatLon}[]).
599
+ @kwarg area: Tolerance (C{meter}, same units as B{C{radius}}).
600
+ @kwarg radius: Mean earth radius (C{meter}).
601
+ @kwarg attr: Optional, points attribute to save the area value
602
+ (C{str}).
603
+ @kwarg indices: If C{True} return the simplified point indices
604
+ instead of the simplified points (C{bool}).
605
+ @kwarg options: Optional keyword arguments passed thru to
606
+ function L{pygeodesy.equirectangular_}.
607
+
608
+ @return: Simplified points (C{LatLon}[]).
609
+
610
+ @raise AttributeError: If an B{C{attr}} is specified for I{Numpy2}
611
+ B{C{points}}.
612
+
613
+ @raise LimitError: Lat- and/or longitudinal delta exceeds the B{C{limit}},
614
+ see function L{pygeodesy.equirectangular_}.
615
+
616
+ @raise ValueError: Tolerance B{C{area}} or B{C{radius}} too small.
617
+ '''
618
+ S = _Sy(points, area, radius, False, indices, **options)
619
+
620
+ if S.vwn() > 2:
621
+ # remove all points with an area
622
+ # not exceeding the tolerance
623
+ S.rm2(S.s2)
624
+
625
+ return S.vwr(attr)
626
+
627
+ # **) MIT License
628
+ #
629
+ # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
630
+ #
631
+ # Permission is hereby granted, free of charge, to any person obtaining a
632
+ # copy of this software and associated documentation files (the "Software"),
633
+ # to deal in the Software without restriction, including without limitation
634
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
635
+ # and/or sell copies of the Software, and to permit persons to whom the
636
+ # Software is furnished to do so, subject to the following conditions:
637
+ #
638
+ # The above copyright notice and this permission notice shall be included
639
+ # in all copies or substantial portions of the Software.
640
+ #
641
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
642
+ # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
643
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
644
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
645
+ # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
646
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
647
+ # OTHER DEALINGS IN THE SOFTWARE.