pygeodesy 24.11.11__py2.py3-none-any.whl → 25.1.5__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {PyGeodesy-24.11.11.dist-info → PyGeodesy-25.1.5.dist-info}/METADATA +34 -35
- PyGeodesy-25.1.5.dist-info/RECORD +118 -0
- {PyGeodesy-24.11.11.dist-info → PyGeodesy-25.1.5.dist-info}/WHEEL +1 -1
- pygeodesy/__init__.py +19 -19
- pygeodesy/__main__.py +1 -1
- pygeodesy/albers.py +5 -5
- pygeodesy/auxilats/_CX_4.py +1 -1
- pygeodesy/auxilats/_CX_6.py +1 -1
- pygeodesy/auxilats/_CX_8.py +1 -1
- pygeodesy/auxilats/_CX_Rs.py +1 -1
- pygeodesy/auxilats/__init__.py +1 -1
- pygeodesy/auxilats/__main__.py +1 -1
- pygeodesy/auxilats/auxAngle.py +5 -5
- pygeodesy/auxilats/auxDLat.py +6 -6
- pygeodesy/auxilats/auxDST.py +2 -2
- pygeodesy/auxilats/auxLat.py +5 -5
- pygeodesy/auxilats/auxily.py +2 -2
- pygeodesy/azimuthal.py +5 -5
- pygeodesy/basics.py +60 -8
- pygeodesy/booleans.py +1 -1
- pygeodesy/cartesianBase.py +22 -61
- pygeodesy/clipy.py +1 -1
- pygeodesy/constants.py +5 -5
- pygeodesy/css.py +1 -1
- pygeodesy/datums.py +1 -1
- pygeodesy/deprecated/__init__.py +2 -2
- pygeodesy/deprecated/bases.py +1 -1
- pygeodesy/deprecated/classes.py +86 -2
- pygeodesy/deprecated/consterns.py +1 -1
- pygeodesy/deprecated/datum.py +5 -5
- pygeodesy/deprecated/functions.py +42 -8
- pygeodesy/deprecated/nvector.py +1 -1
- pygeodesy/deprecated/rhumbBase.py +1 -1
- pygeodesy/deprecated/rhumbaux.py +1 -1
- pygeodesy/deprecated/rhumbsolve.py +1 -1
- pygeodesy/deprecated/rhumbx.py +1 -1
- pygeodesy/dms.py +1 -1
- pygeodesy/ecef.py +53 -56
- pygeodesy/elevations.py +1 -1
- pygeodesy/ellipsoidalBase.py +3 -3
- pygeodesy/ellipsoidalBaseDI.py +1 -1
- pygeodesy/ellipsoidalExact.py +1 -1
- pygeodesy/ellipsoidalGeodSolve.py +1 -1
- pygeodesy/ellipsoidalKarney.py +1 -1
- pygeodesy/ellipsoidalNvector.py +1 -1
- pygeodesy/ellipsoidalVincenty.py +6 -5
- pygeodesy/ellipsoids.py +4 -5
- pygeodesy/elliptic.py +6 -6
- pygeodesy/epsg.py +1 -1
- pygeodesy/errors.py +1 -1
- pygeodesy/etm.py +5 -5
- pygeodesy/fmath.py +18 -17
- pygeodesy/formy.py +409 -555
- pygeodesy/frechet.py +29 -86
- pygeodesy/fstats.py +1 -1
- pygeodesy/fsums.py +32 -33
- pygeodesy/gars.py +1 -1
- pygeodesy/geodesici.py +7 -7
- pygeodesy/geodesicw.py +1 -1
- pygeodesy/geodesicx/_C4_24.py +2 -2
- pygeodesy/geodesicx/_C4_27.py +2 -2
- pygeodesy/geodesicx/_C4_30.py +2 -2
- pygeodesy/geodesicx/__init__.py +2 -2
- pygeodesy/geodesicx/__main__.py +4 -5
- pygeodesy/geodesicx/gx.py +6 -5
- pygeodesy/geodesicx/gxarea.py +2 -2
- pygeodesy/geodesicx/gxbases.py +2 -2
- pygeodesy/geodesicx/gxline.py +16 -12
- pygeodesy/geodsolve.py +1 -1
- pygeodesy/geohash.py +1 -1
- pygeodesy/geoids.py +277 -203
- pygeodesy/hausdorff.py +23 -81
- pygeodesy/heights.py +115 -150
- pygeodesy/internals.py +1 -1
- pygeodesy/interns.py +2 -3
- pygeodesy/iters.py +1 -1
- pygeodesy/karney.py +3 -3
- pygeodesy/ktm.py +16 -15
- pygeodesy/latlonBase.py +323 -409
- pygeodesy/lazily.py +53 -44
- pygeodesy/lcc.py +1 -1
- pygeodesy/ltp.py +46 -50
- pygeodesy/ltpTuples.py +147 -130
- pygeodesy/mgrs.py +1 -1
- pygeodesy/named.py +149 -3
- pygeodesy/namedTuples.py +58 -7
- pygeodesy/nvectorBase.py +122 -105
- pygeodesy/osgr.py +1 -1
- pygeodesy/points.py +1 -1
- pygeodesy/props.py +1 -1
- pygeodesy/resections.py +18 -17
- pygeodesy/rhumb/__init__.py +1 -1
- pygeodesy/rhumb/aux_.py +2 -2
- pygeodesy/rhumb/bases.py +2 -2
- pygeodesy/rhumb/ekx.py +4 -4
- pygeodesy/rhumb/solve.py +1 -1
- pygeodesy/simplify.py +289 -401
- pygeodesy/solveBase.py +1 -1
- pygeodesy/sphericalBase.py +1 -1
- pygeodesy/sphericalNvector.py +5 -5
- pygeodesy/sphericalTrigonometry.py +7 -6
- pygeodesy/streprs.py +10 -5
- pygeodesy/trf.py +1 -1
- pygeodesy/triaxials.py +23 -16
- pygeodesy/units.py +16 -16
- pygeodesy/unitsBase.py +1 -1
- pygeodesy/ups.py +4 -4
- pygeodesy/utily.py +341 -211
- pygeodesy/utm.py +5 -5
- pygeodesy/utmups.py +1 -1
- pygeodesy/utmupsBase.py +1 -1
- pygeodesy/vector2d.py +5 -5
- pygeodesy/vector3d.py +14 -3
- pygeodesy/vector3dBase.py +5 -5
- pygeodesy/webmercator.py +1 -1
- pygeodesy/wgrs.py +1 -1
- PyGeodesy-24.11.11.dist-info/RECORD +0 -118
- {PyGeodesy-24.11.11.dist-info → PyGeodesy-25.1.5.dist-info}/top_level.txt +0 -0
pygeodesy/simplify.py
CHANGED
|
@@ -1,59 +1,53 @@
|
|
|
1
1
|
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
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
|
-
|
|
9
|
-
|
|
10
|
-
Function L{simplify1} eliminates points
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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.
|
|
4
|
+
u'''Simplify or linearize a path of C{LatLon} points.
|
|
5
|
+
|
|
6
|
+
Each of the 4 I{simplify} functions is based on a different algorithm and
|
|
7
|
+
produces different, simplified results in (very) different run times for the
|
|
8
|
+
same path:
|
|
9
|
+
|
|
10
|
+
- Function L{simplify1} eliminates points with edge lengths shorter than
|
|
11
|
+
the given tolerance.
|
|
12
|
+
|
|
13
|
+
- Function L{simplifyRDP} implements the I{Ramer-Douglas-Peucker} (RDP)
|
|
14
|
+
algorithm, iteratively finding the point farthest from each path edge.
|
|
15
|
+
Original RDP exhaustively searches the most distant point in each iteration,
|
|
16
|
+
I{modified} RDP stops at the first point exceeding the distance tolerance.
|
|
17
|
+
|
|
18
|
+
- Function L{simplifyRW} uses the I{Reumann-Witkam} (RW) method, sliding a
|
|
19
|
+
"pipe" over each path edge, removing all subsequent points within the pipe
|
|
20
|
+
radius, up to the first point outside the pipe.
|
|
21
|
+
|
|
22
|
+
- Function L{simplifyVW} provides the I{Visvalingam-Whyatt} (VW) method
|
|
23
|
+
using the area of the triangle formed by three neigboring points. Original
|
|
24
|
+
VW removes only a single point per iteration, I{modified} VW eliminates all
|
|
25
|
+
points with a triangular area not exceeding the tolerance in each iteration.
|
|
26
|
+
|
|
27
|
+
Keyword argument I{shortest} of functions L{simplifyRDP} and L{simplifyRW}
|
|
28
|
+
specifies of the distance between a point and a path edge. If C{True}, use
|
|
29
|
+
the I{shortest} distance to the path edge or edge points, otherwise use the
|
|
30
|
+
I{perpendicular} distance to the (extended) edge through both points.
|
|
36
31
|
|
|
37
32
|
Keyword argument B{C{radius}} of all fuctions is set to the mean earth
|
|
38
|
-
radius in C{meter}. Other units
|
|
39
|
-
and tolerance are
|
|
33
|
+
radius in C{meter}, conventionally. Other units may be used, provided
|
|
34
|
+
that radius and tolerance are specified in the same units.
|
|
40
35
|
|
|
41
|
-
Use keyword argument C{B{indices}=True} in any function to return a
|
|
42
|
-
|
|
43
|
-
|
|
36
|
+
Use keyword argument C{B{indices}=True} in any function to return a list
|
|
37
|
+
of I{indices} of simplified point instead of the simplified points with
|
|
38
|
+
the first and last index are always the first and last original index.
|
|
44
39
|
|
|
45
40
|
Finally, any additional keyword arguments B{C{options}} to all functions
|
|
46
41
|
are passed thru to function L{pygeodesy.equirectangular4} to specify the
|
|
47
42
|
distance approximation.
|
|
48
43
|
|
|
49
|
-
To process C{NumPy} arrays containing rows of lat-, longitude and
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
subset.
|
|
44
|
+
To process C{NumPy} arrays containing rows of lat-, longitude and possibly
|
|
45
|
+
other values, use class L{Numpy2LatLon} to wrap the C{NumPy} array into
|
|
46
|
+
I{on-the-fly-LatLon} points. Pass the L{Numpy2LatLon} instance to any
|
|
47
|
+
I{simplify} function and the returned result will be a C{NumPy} array
|
|
48
|
+
containing the simplified subset, a partial copy of the original C{NumPy}
|
|
49
|
+
array. Use keyword argument C{B{indices}=True} to return a list of array
|
|
50
|
+
row indices inlieu of the simplified array subset.
|
|
57
51
|
|
|
58
52
|
See:
|
|
59
53
|
- U{https://Bost.Ocks.org/mike/simplify}
|
|
@@ -75,7 +69,7 @@ from __future__ import division as _; del _ # PYCHOK semicolon
|
|
|
75
69
|
|
|
76
70
|
# from pygeodesy.basics import len2 # from .fmath
|
|
77
71
|
from pygeodesy.constants import EPS, R_M, _1_0
|
|
78
|
-
from pygeodesy.errors import _AttributeError, _ValueError
|
|
72
|
+
from pygeodesy.errors import _AttributeError, _ValueError, _xkwds_pop2
|
|
79
73
|
from pygeodesy.fmath import fdot_, len2, sqrt0
|
|
80
74
|
from pygeodesy.formy import equirectangular4
|
|
81
75
|
from pygeodesy.interns import _small_, _too_
|
|
@@ -86,7 +80,7 @@ from pygeodesy.units import _ALL_LAZY, _1mm, Radius_
|
|
|
86
80
|
from math import degrees, fabs, radians
|
|
87
81
|
|
|
88
82
|
__all__ = _ALL_LAZY.simplify
|
|
89
|
-
__version__ = '24.
|
|
83
|
+
__version__ = '24.12.02'
|
|
90
84
|
|
|
91
85
|
|
|
92
86
|
# try:
|
|
@@ -101,7 +95,7 @@ __version__ = '24.11.07'
|
|
|
101
95
|
# self is not first method argument" which can't
|
|
102
96
|
# be suppressed with command line option --stdlib
|
|
103
97
|
class _T2(object):
|
|
104
|
-
'''(INTERNAL) VW 2-tuple (index,
|
|
98
|
+
'''(INTERNAL) VW 2-tuple (index, area2).
|
|
105
99
|
'''
|
|
106
100
|
# __slots__ are no longer space savers, see
|
|
107
101
|
# the comments at the class .points.LatLon_
|
|
@@ -115,18 +109,19 @@ class _T2(object):
|
|
|
115
109
|
class _Sy(object):
|
|
116
110
|
'''(INTERNAL) Simplify state.
|
|
117
111
|
'''
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
n
|
|
123
|
-
options
|
|
124
|
-
pts
|
|
125
|
-
radius
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
subset
|
|
112
|
+
d2yxse5 = () # 5-tuple
|
|
113
|
+
eps = EPS # system epsilon
|
|
114
|
+
indices = False
|
|
115
|
+
ixs = set() # set(indices)
|
|
116
|
+
n = 0
|
|
117
|
+
options = {}
|
|
118
|
+
pts = []
|
|
119
|
+
radius = R_M # mean earth radius
|
|
120
|
+
s2 = EPS # tolerance squared
|
|
121
|
+
s2e = EPS # VW sentinel
|
|
122
|
+
shortest = False # i.e. perpendicular
|
|
123
|
+
subset = None # isNumpy2 or isTuple2
|
|
124
|
+
t2s = [] # list(_T2s)
|
|
130
125
|
|
|
131
126
|
def __init__(self, points, tolerance, radius, shortest,
|
|
132
127
|
indices, **options):
|
|
@@ -134,123 +129,99 @@ class _Sy(object):
|
|
|
134
129
|
'''
|
|
135
130
|
n, self.pts = len2(points)
|
|
136
131
|
if n > 0:
|
|
137
|
-
self.n
|
|
138
|
-
self.
|
|
132
|
+
self.n = n
|
|
133
|
+
self.ixs = set((0, n-1))
|
|
139
134
|
|
|
140
|
-
if
|
|
141
|
-
self.subset = points.subset
|
|
142
|
-
|
|
143
|
-
if indices:
|
|
144
|
-
self.indices = True
|
|
145
|
-
|
|
146
|
-
if radius:
|
|
135
|
+
if radius is not R_M:
|
|
147
136
|
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
137
|
# tolerance converted to degrees squared
|
|
155
|
-
self.s2 = degrees(tolerance / self.radius)**2
|
|
156
|
-
if min(
|
|
138
|
+
self.s2 = s2 = degrees(tolerance / self.radius)**2
|
|
139
|
+
if min(s2, tolerance) < self.eps:
|
|
157
140
|
raise _ValueError(tolerance=tolerance, txt=_too_(_small_))
|
|
158
|
-
self.s2e =
|
|
141
|
+
self.s2e = s2 + _1_0 # VW sentinel
|
|
142
|
+
# assert self.s2e > s2
|
|
159
143
|
|
|
160
|
-
|
|
161
|
-
|
|
144
|
+
if indices:
|
|
145
|
+
self.indices = True
|
|
146
|
+
if options:
|
|
147
|
+
_, self.options = _xkwds_pop2(options, modified=None)
|
|
148
|
+
if shortest:
|
|
149
|
+
self.shortest = True
|
|
150
|
+
if isNumpy2(points) or isTuple2(points): # NOT self.pts
|
|
151
|
+
self.subset = points.subset
|
|
162
152
|
|
|
163
153
|
def d21(self, s, e):
|
|
164
|
-
'''Set path edge or line thru points[s]
|
|
154
|
+
'''Set path edge or line thru (points[s], -[e]).
|
|
165
155
|
'''
|
|
166
156
|
d21, y21, x21, _ = self.d2yxu4(s, e)
|
|
167
157
|
self.d2yxse5 = d21, y21, x21, s, e
|
|
168
158
|
return d21 > self.eps
|
|
169
159
|
|
|
170
|
-
def
|
|
171
|
-
'''Find the tallest distance among all points[n
|
|
172
|
-
to points[s]
|
|
160
|
+
def d2i2(self, m, n, modified):
|
|
161
|
+
'''Find the tallest distance among all points[m..n]
|
|
162
|
+
to (points[s], -[e]) exceeding the tolerance.
|
|
173
163
|
'''
|
|
174
164
|
_, _, _, s, _ = self.d2yxse5
|
|
175
|
-
eps, _d2yxu4 = self.eps, self.d2yxu4
|
|
176
165
|
t2, t = self.s2, 0 # tallest
|
|
177
|
-
for i in range(
|
|
178
|
-
d2, _, _, _ =
|
|
166
|
+
for i in range(m, n):
|
|
167
|
+
d2, _, _, _ = self.d2yxu4(s, i)
|
|
179
168
|
if d2 > t2:
|
|
180
169
|
t2, t = d2, i
|
|
181
|
-
if
|
|
170
|
+
if modified and d2 > self.eps:
|
|
182
171
|
break
|
|
183
172
|
return t2, t
|
|
184
173
|
|
|
185
|
-
def
|
|
186
|
-
'''Find the tallest I{perpendicular} distance
|
|
187
|
-
points[n
|
|
188
|
-
|
|
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.
|
|
174
|
+
def d2ix2(self, m, n, modified):
|
|
175
|
+
'''Find the tallest I{perpendicular B{or} shortest} distance
|
|
176
|
+
among all points[m..n] to the path edge or line through
|
|
177
|
+
(points[s], -[e]) exceeding the tolerance.
|
|
208
178
|
'''
|
|
179
|
+
h = not self.shortest
|
|
209
180
|
# point (x, y) on axis rotated by angle a ccw:
|
|
210
181
|
# x' = y * sin(a) + x * cos(a)
|
|
211
182
|
# y' = y * cos(a) - x * sin(a)
|
|
212
183
|
#
|
|
213
|
-
# distance (w)
|
|
214
|
-
# a line
|
|
184
|
+
# distance along (w) and perpendicular to (h)
|
|
185
|
+
# a line from the origin to point (dx, dy):
|
|
215
186
|
# w = (y * dy + x * dx) / hypot(dx, dy)
|
|
216
187
|
# h = (y * dx - x * dy) / hypot(dx, dy)
|
|
217
|
-
|
|
218
188
|
d21, y21, x21, s, e = self.d2yxse5
|
|
219
|
-
eps, _d2yxu4 = self.eps, self.d2yxu4
|
|
220
189
|
t2, t = self.s2, 0 # tallest
|
|
221
|
-
for i in range(
|
|
222
|
-
# distance points[
|
|
223
|
-
d2, y01, x01, _ =
|
|
224
|
-
if d2 > eps:
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
190
|
+
for i in range(m, n):
|
|
191
|
+
# distance points[s] to -[i], ...
|
|
192
|
+
d2, y01, x01, _ = self.d2yxu4(s, i)
|
|
193
|
+
if d2 > self.eps:
|
|
194
|
+
if h: # perpendicular distance
|
|
195
|
+
d2 = fdot_(y01, x21, -x01, y21)**2 / d21
|
|
196
|
+
else:
|
|
197
|
+
w = fdot_(y01, y21, x01, x21)
|
|
198
|
+
if w > 0:
|
|
199
|
+
if w < d21: # ... perpendicular ...
|
|
200
|
+
d2 = fdot_(y01, x21, -x01, y21)**2 / d21
|
|
201
|
+
else: # ... or points[e] to -[i]
|
|
202
|
+
d2, _, _, _ = self.d2yxu4(e, i)
|
|
232
203
|
if d2 > t2:
|
|
233
204
|
t2, t = d2, i
|
|
234
|
-
if
|
|
205
|
+
if modified:
|
|
235
206
|
break
|
|
236
207
|
return t2, t
|
|
237
208
|
|
|
238
209
|
def d2yxu4(self, i, j):
|
|
239
|
-
'''Return distance I{squared},
|
|
240
|
-
|
|
210
|
+
'''Return the distance I{squared}, the deltas and the
|
|
211
|
+
(longitudinal) unrollment between (points[i], -[j]).
|
|
241
212
|
'''
|
|
242
|
-
p1, p2= self.pts[i], self.pts[j]
|
|
213
|
+
p1, p2 = self.pts[i], self.pts[j]
|
|
243
214
|
return equirectangular4(p1.lat, p1.lon,
|
|
244
215
|
p2.lat, p2.lon, **self.options)
|
|
245
216
|
|
|
246
|
-
def h2t(self, i1,
|
|
247
|
-
'''Compute the
|
|
248
|
-
|
|
249
|
-
|
|
217
|
+
def h2t(self, i1, i2, i3):
|
|
218
|
+
'''Compute (double) the triangle area, points[i2] is
|
|
219
|
+
the top and edge (points[i1], -[i3]) is the base
|
|
220
|
+
of the triangle.
|
|
250
221
|
'''
|
|
251
|
-
d21, y21, x21 , _= self.d2yxu4(i1,
|
|
222
|
+
d21, y21, x21 , _ = self.d2yxu4(i1, i3)
|
|
252
223
|
if d21 > self.eps:
|
|
253
|
-
d01, y01, x01, _ = self.d2yxu4(i1,
|
|
224
|
+
d01, y01, x01, _ = self.d2yxu4(i1, i2)
|
|
254
225
|
if d01 > self.eps:
|
|
255
226
|
h2 = fabs(fdot_(y01, x21, -x01, y21))
|
|
256
227
|
# triangle height h = h2 / sqrt(d21) and
|
|
@@ -258,137 +229,171 @@ class _Sy(object):
|
|
|
258
229
|
return h2 # double triangle area
|
|
259
230
|
return 0
|
|
260
231
|
|
|
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
232
|
def rdp(self, modified):
|
|
273
233
|
'''Ramer-Douglas-Peucker (RDP) simplification of a
|
|
274
234
|
path of C{LatLon} points.
|
|
275
235
|
|
|
276
|
-
@arg modified: Use modified RDP (C{bool}).
|
|
236
|
+
@arg modified: Use I{modified} RDP (C{bool}).
|
|
277
237
|
'''
|
|
278
|
-
|
|
238
|
+
r, n = self.ixs, self.n
|
|
279
239
|
if n > 1:
|
|
280
|
-
s2,
|
|
281
|
-
_d2i2, _d2ih2 = self.d2i2, self.d2ih2
|
|
282
|
-
|
|
283
|
-
se = [(0, n-1)]
|
|
284
|
-
_a = se.append
|
|
285
|
-
_p = se.pop
|
|
240
|
+
s2, se = self.s2, [(0, n-1)]
|
|
286
241
|
while se:
|
|
287
|
-
s, e =
|
|
242
|
+
s, e = se.pop()
|
|
288
243
|
s1 = s + 1
|
|
289
244
|
if e > s1:
|
|
290
|
-
if
|
|
291
|
-
d2, i =
|
|
245
|
+
if self.d21(s, e): # points[] to edge [s, e]
|
|
246
|
+
d2, i = self.d2ix2(s1, e, modified)
|
|
292
247
|
else: # points[] to point [s]
|
|
293
|
-
d2, i =
|
|
248
|
+
d2, i = self.d2i2( s1, e, modified)
|
|
294
249
|
if d2 > s2 and i > 0:
|
|
295
|
-
|
|
296
|
-
_a((i, e))
|
|
250
|
+
se.append((i, e))
|
|
297
251
|
if not modified:
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
return self.
|
|
252
|
+
se.append((s, i))
|
|
253
|
+
r.add(i)
|
|
254
|
+
r.add(s)
|
|
255
|
+
return self.result(r)
|
|
302
256
|
|
|
303
|
-
def
|
|
304
|
-
'''
|
|
305
|
-
the trangular area of both neighboring points, but
|
|
306
|
-
removes those too unless the recomputed area exceeds
|
|
307
|
-
the tolerance.
|
|
257
|
+
def result(self, r):
|
|
258
|
+
'''Return the simplified points or indices.
|
|
308
259
|
'''
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
r[n].h2 = h2
|
|
317
|
-
break # while
|
|
318
|
-
else:
|
|
319
|
-
r.pop(n)
|
|
260
|
+
r = sorted(r)
|
|
261
|
+
if self.indices:
|
|
262
|
+
return list(r)
|
|
263
|
+
elif self.subset:
|
|
264
|
+
return self.subset(r)
|
|
265
|
+
else:
|
|
266
|
+
return list(self.pts[i] for i in r)
|
|
320
267
|
|
|
321
|
-
def
|
|
322
|
-
'''
|
|
323
|
-
triangular area not exceeding the tolerance.
|
|
268
|
+
def rw(self):
|
|
269
|
+
'''Reumann-Witkam simplification.
|
|
324
270
|
'''
|
|
325
|
-
r,
|
|
271
|
+
r, n = self.ixs, self.n
|
|
272
|
+
if n > 1:
|
|
273
|
+
s, e, s2 = 0, 1, self.s2
|
|
274
|
+
while s < e < n:
|
|
275
|
+
if self.d21(s, e):
|
|
276
|
+
d2, i = self.d2ix2(e + 1, n, True)
|
|
277
|
+
r.add(s)
|
|
278
|
+
if d2 > s2 and i > 0:
|
|
279
|
+
r.add(i)
|
|
280
|
+
s = e = i
|
|
281
|
+
else:
|
|
282
|
+
# r.add(n - 1)
|
|
283
|
+
break
|
|
284
|
+
e += 1
|
|
285
|
+
return self.result(r)
|
|
326
286
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
287
|
+
def sy1(self):
|
|
288
|
+
'''Basic simplification.
|
|
289
|
+
'''
|
|
290
|
+
r, n = self.ixs, self.n
|
|
291
|
+
if n > 1:
|
|
292
|
+
s2, i = self.s2, 0
|
|
293
|
+
for j in range(1, n):
|
|
294
|
+
d2, _, _, _ = self.d2yxu4(i, j)
|
|
295
|
+
if d2 > s2:
|
|
296
|
+
r.add(j)
|
|
297
|
+
i = j
|
|
298
|
+
return self.result(r)
|
|
333
299
|
|
|
334
300
|
def vwn(self):
|
|
335
|
-
'''Initialize
|
|
336
|
-
|
|
337
|
-
|
|
301
|
+
'''Initialize VW as list of 2-tuples _T2(ix, h2) where
|
|
302
|
+
ix is the points[] index and h2 is the triangular
|
|
303
|
+
area I{(times 2)} of that point.
|
|
338
304
|
'''
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
self.r = r = []
|
|
305
|
+
self.t2s = t = []
|
|
306
|
+
n, T2 = self.n, _T2
|
|
342
307
|
if n > 2:
|
|
343
|
-
|
|
308
|
+
_h2t = self.h2t
|
|
309
|
+
t[:] = [T2(i, _h2t(i-1, i, i+1)) for i in range(1, n - 1)]
|
|
344
310
|
if n > 1:
|
|
345
|
-
|
|
311
|
+
t.append(T2(n - 1, self.s2e))
|
|
346
312
|
if n > 0:
|
|
347
|
-
|
|
348
|
-
return len(
|
|
313
|
+
t.insert(0, T2(0, self.s2e))
|
|
314
|
+
return len(t)
|
|
349
315
|
|
|
350
316
|
def vwr(self, attr):
|
|
351
|
-
'''Return the
|
|
352
|
-
|
|
353
|
-
|
|
317
|
+
'''Return the VW results, optionally including the
|
|
318
|
+
triangular area (in C{meter}) as attribute C{attr}
|
|
319
|
+
to each simplified point.
|
|
354
320
|
'''
|
|
355
|
-
pts,
|
|
321
|
+
pts, t = self.pts, self.t2s
|
|
356
322
|
|
|
357
323
|
# double check the minimal triangular area
|
|
358
|
-
assert min(t2.h2 for t2 in
|
|
324
|
+
assert min(t2.h2 for t2 in t) > self.s2 > 0
|
|
359
325
|
|
|
360
|
-
if attr: # return
|
|
326
|
+
if attr: # return each trangular area (actually
|
|
361
327
|
# the sqrt of double the triangular area)
|
|
362
328
|
# converted back from degrees to meter
|
|
363
329
|
if isNumpy2(pts):
|
|
364
330
|
raise _AttributeError(attr=attr)
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
for t2 in
|
|
331
|
+
t[0].h2 = t[-1].h2 = 0 # zap sentinels
|
|
332
|
+
m = radians(_1_0) * self.radius # meter
|
|
333
|
+
for t2 in t: # convert back to meter
|
|
368
334
|
setattr(pts[t2.ix], attr, sqrt0(t2.h2) * m)
|
|
369
335
|
|
|
370
|
-
# double check for duplicates
|
|
371
|
-
|
|
372
|
-
r = dict((t2.ix, True) for t2 in r)
|
|
336
|
+
n = len(t) # double check for duplicates
|
|
337
|
+
r = set(t2.ix for t2 in t)
|
|
373
338
|
assert len(r) == n
|
|
374
|
-
return self.
|
|
339
|
+
return self.result(r)
|
|
375
340
|
|
|
341
|
+
def vwrm(self):
|
|
342
|
+
'''Keep removing the VW point with the smallest triangular
|
|
343
|
+
area until that area exceeds the tolerance.
|
|
344
|
+
'''
|
|
345
|
+
s2, t = self.s2, self.t2s
|
|
346
|
+
while len(t) > 2:
|
|
347
|
+
m2, m = t[1].h2, 1
|
|
348
|
+
for i in range(2, len(t) - 1):
|
|
349
|
+
h2 = t[i].h2
|
|
350
|
+
if h2 < m2:
|
|
351
|
+
m2, m = h2, i
|
|
352
|
+
if m2 > s2:
|
|
353
|
+
break
|
|
354
|
+
self.vwrm1(m, 0)
|
|
376
355
|
|
|
377
|
-
def
|
|
378
|
-
|
|
356
|
+
def vwrm1(self, m, tol):
|
|
357
|
+
'''Eliminate VW point[m], keep recomputing the trangular
|
|
358
|
+
area of both neighboring points and removing those
|
|
359
|
+
too until the recomputed area exceeds C{tol}.
|
|
360
|
+
'''
|
|
361
|
+
t, _h2t = self.t2s, self.h2t
|
|
362
|
+
t.pop(m)
|
|
363
|
+
for n in (m, m - 1): # neighbors
|
|
364
|
+
while 0 < n < (len(t) - 1):
|
|
365
|
+
h2 = _h2t(t[n-1].ix, t[n].ix, t[n+1].ix)
|
|
366
|
+
if h2 > tol:
|
|
367
|
+
t[n].h2 = h2
|
|
368
|
+
break # while
|
|
369
|
+
t.pop(n)
|
|
370
|
+
|
|
371
|
+
def vwrm2(self, tol):
|
|
372
|
+
'''Eliminate all VW points with a triangular area not
|
|
373
|
+
exceeding C{tol}.
|
|
374
|
+
'''
|
|
375
|
+
t = self.t2s
|
|
376
|
+
m = len(t) - 1
|
|
377
|
+
while m > 1:
|
|
378
|
+
m -= 1
|
|
379
|
+
if t[m].h2 <= tol:
|
|
380
|
+
self.vwrm1(m, tol)
|
|
381
|
+
m = min(m, len(t) - 1)
|
|
379
382
|
|
|
380
|
-
Eliminates any points closer together than the given I{distance}
|
|
381
|
-
tolerance.
|
|
382
383
|
|
|
383
|
-
|
|
384
|
+
def simplify1(points, distance=_1mm, radius=R_M, indices=False, **options):
|
|
385
|
+
'''Basic simplification of a path of C{LatLon} points by eliminating
|
|
386
|
+
any points closer together than the given I{distance} tolerance.
|
|
387
|
+
|
|
388
|
+
@arg points: Iterable with the path points (C{LatLon}[]).
|
|
384
389
|
@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
|
|
387
|
-
|
|
388
|
-
@kwarg options: Optional keyword arguments passed thru to
|
|
389
|
-
|
|
390
|
+
@kwarg radius: Mean earth radius (C{meter}, conventionally).
|
|
391
|
+
@kwarg indices: If C{True}, return B{C{points}} indices instead
|
|
392
|
+
of the simplified points (C{bool}).
|
|
393
|
+
@kwarg options: Optional keyword arguments passed thru to function
|
|
394
|
+
L{pygeodesy.equirectangular4}.
|
|
390
395
|
|
|
391
|
-
@return: Simplified points (C{LatLon}[]).
|
|
396
|
+
@return: Simplified points (C{LatLon}[]) or B{C{points}} indices.
|
|
392
397
|
|
|
393
398
|
@raise LimitError: Lat- and/or longitudinal delta exceeds the B{C{limit}},
|
|
394
399
|
see function L{pygeodesy.equirectangular4}.
|
|
@@ -396,109 +401,63 @@ def simplify1(points, distance=_1mm, radius=R_M, indices=False, **options):
|
|
|
396
401
|
@raise ValueError: Tolerance B{C{distance}} or B{C{radius}} too small.
|
|
397
402
|
'''
|
|
398
403
|
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)
|
|
404
|
+
return S.sy1()
|
|
412
405
|
|
|
413
406
|
|
|
414
407
|
def simplifyRDP(points, distance=_1mm, radius=R_M, shortest=False,
|
|
415
|
-
|
|
416
|
-
'''I{Ramer-Douglas-Peucker} (RDP) simplification of a path of
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
Eliminates any points too close together or closer to an
|
|
408
|
+
indices=False, modified=False, **options):
|
|
409
|
+
'''I{Ramer-Douglas-Peucker} (RDP) simplification of a path of C{LatLon}
|
|
410
|
+
points by eliminating any points too close together or closer to an
|
|
420
411
|
edge than the given I{distance} tolerance.
|
|
421
412
|
|
|
422
|
-
|
|
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}[]).
|
|
413
|
+
@arg points: Iterable with the path points (C{LatLon}[]).
|
|
427
414
|
@kwarg distance: Tolerance (C{meter}, same units as B{C{radius}}).
|
|
428
|
-
@kwarg radius: Mean earth radius (C{meter}).
|
|
415
|
+
@kwarg radius: Mean earth radius (C{meter}, conventionally).
|
|
429
416
|
@kwarg shortest: If C{True}, use the I{shortest} otherwise the
|
|
430
417
|
I{perpendicular} distance (C{bool}).
|
|
431
|
-
@kwarg indices: If C{True}, return
|
|
432
|
-
|
|
433
|
-
@kwarg
|
|
434
|
-
|
|
418
|
+
@kwarg indices: If C{True}, return B{C{points}} indices instead
|
|
419
|
+
of the simplified points (C{bool}).
|
|
420
|
+
@kwarg modified: If C{True}, use the C{modified RDP} method (C{bool}),
|
|
421
|
+
see the B{note}.
|
|
422
|
+
@kwarg options: Optional keyword arguments passed thru to function
|
|
423
|
+
L{pygeodesy.equirectangular4}.
|
|
435
424
|
|
|
436
|
-
@return: Simplified points (C{LatLon}[]).
|
|
425
|
+
@return: Simplified points (C{LatLon}[]) or B{C{points}} indices.
|
|
437
426
|
|
|
438
427
|
@raise LimitError: Lat- and/or longitudinal delta exceeds the B{C{limit}},
|
|
439
428
|
see function L{pygeodesy.equirectangular4}.
|
|
440
429
|
|
|
441
430
|
@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
431
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
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.equirectangular4}.
|
|
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.equirectangular4}.
|
|
474
|
-
|
|
475
|
-
@raise ValueError: Tolerance B{C{distance}} or B{C{radius}} too small.
|
|
432
|
+
@note: The original C{RDP} method exhaustively searches for the point
|
|
433
|
+
with the largest distance (resulting in complexity M{O(n**2)}
|
|
434
|
+
with M{n} is the number of points). The B{C{modified}} C{RDP}
|
|
435
|
+
method stops at the first point farther than the B{C{distance}}
|
|
436
|
+
tolerance, significantly reducing the run time (but producing
|
|
437
|
+
results different from the original C{RDP} method).
|
|
476
438
|
'''
|
|
477
439
|
S = _Sy(points, distance, radius, shortest, indices, **options)
|
|
478
|
-
|
|
479
|
-
return S.rdp(True)
|
|
440
|
+
return S.rdp(bool(modified))
|
|
480
441
|
|
|
481
442
|
|
|
482
443
|
def simplifyRW(points, pipe=_1mm, radius=R_M, shortest=False,
|
|
483
444
|
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
|
|
445
|
+
'''I{Reumann-Witkam} (RW) simplification of a path of C{LatLon} points
|
|
446
|
+
by eliminating any points too close together or within the given
|
|
488
447
|
I{pipe} tolerance along an edge.
|
|
489
448
|
|
|
490
|
-
@arg points:
|
|
449
|
+
@arg points: Iterable with the path points (C{LatLon}[]).
|
|
491
450
|
@kwarg pipe: Pipe radius, half-width (C{meter}, same units as
|
|
492
451
|
B{C{radius}}).
|
|
493
|
-
@kwarg radius: Mean earth radius (C{meter}).
|
|
452
|
+
@kwarg radius: Mean earth radius (C{meter}, conventionally).
|
|
494
453
|
@kwarg shortest: If C{True}, use the I{shortest} otherwise the
|
|
495
454
|
I{perpendicular} distance (C{bool}).
|
|
496
|
-
@kwarg indices: If C{True}, return
|
|
497
|
-
|
|
498
|
-
@kwarg options: Optional keyword arguments passed thru to
|
|
499
|
-
|
|
455
|
+
@kwarg indices: If C{True}, return B{C{points}} indices instead
|
|
456
|
+
of the simplified points (C{bool}).
|
|
457
|
+
@kwarg options: Optional keyword arguments passed thru to function
|
|
458
|
+
L{pygeodesy.equirectangular4}.
|
|
500
459
|
|
|
501
|
-
@return: Simplified points (C{LatLon}[]).
|
|
460
|
+
@return: Simplified points (C{LatLon}[]) or B{C{points}} indices.
|
|
502
461
|
|
|
503
462
|
@raise LimitError: Lat- and/or longitudinal delta exceeds the B{C{limit}},
|
|
504
463
|
see function L{pygeodesy.equirectangular4}.
|
|
@@ -506,127 +465,56 @@ def simplifyRW(points, pipe=_1mm, radius=R_M, shortest=False,
|
|
|
506
465
|
@raise ValueError: Tolerance B{C{pipe}} or B{C{radius}} too small.
|
|
507
466
|
'''
|
|
508
467
|
S = _Sy(points, pipe, radius, shortest, indices, **options)
|
|
468
|
+
return S.rw()
|
|
509
469
|
|
|
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
470
|
|
|
535
|
-
|
|
471
|
+
def simplifyVW(points, area=_1mm, radius=R_M, indices=False,
|
|
472
|
+
attr=None, modified=False, **options):
|
|
473
|
+
'''I{Visvalingam-Whyatt} (VW) simplification of a path of C{LatLon}
|
|
474
|
+
points by eliminating any points too close or with a triangular
|
|
536
475
|
area not exceeding the given I{area} tolerance I{squared}.
|
|
537
476
|
|
|
538
|
-
|
|
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}[]).
|
|
477
|
+
@arg points: Iterable with the path points (C{LatLon}[]).
|
|
543
478
|
@kwarg area: Tolerance (C{meter}, same units as B{C{radius}}).
|
|
544
|
-
@kwarg radius: Mean earth radius (C{meter}).
|
|
545
|
-
@kwarg
|
|
546
|
-
|
|
547
|
-
@kwarg
|
|
548
|
-
|
|
549
|
-
@kwarg
|
|
550
|
-
|
|
479
|
+
@kwarg radius: Mean earth radius (C{meter}, conventionally).
|
|
480
|
+
@kwarg indices: If C{True}, return B{C{points}} indices instead
|
|
481
|
+
of the simplified points (C{bool}).
|
|
482
|
+
@kwarg attr: Optional, B{C{points}} attribute to save the area
|
|
483
|
+
value (C{str}).
|
|
484
|
+
@kwarg modified: If C{True}, use the C{modified VW} method (C{bool}),
|
|
485
|
+
see the B{note}.
|
|
486
|
+
@kwarg options: Optional keyword arguments passed thru to function
|
|
487
|
+
L{pygeodesy.equirectangular4}.
|
|
551
488
|
|
|
552
|
-
@return: Simplified points (C{LatLon}[]).
|
|
489
|
+
@return: Simplified points (C{LatLon}[]) or B{C{points}} indices.
|
|
553
490
|
|
|
554
|
-
@raise AttributeError:
|
|
555
|
-
B{C{points}}.
|
|
491
|
+
@raise AttributeError: An B{C{attr}} isinvalid for I{Numpy2} B{C{points}}.
|
|
556
492
|
|
|
557
493
|
@raise LimitError: Lat- and/or longitudinal delta exceeds the B{C{limit}},
|
|
558
494
|
see function L{pygeodesy.equirectangular4}.
|
|
559
495
|
|
|
560
496
|
@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
497
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
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.equirectangular4}.
|
|
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.equirectangular4}.
|
|
615
|
-
|
|
616
|
-
@raise ValueError: Tolerance B{C{area}} or B{C{radius}} too small.
|
|
498
|
+
@note: The original C{VW} method exhaustively searches for the point
|
|
499
|
+
with the smallest triangular I{area} (resulting in complexity
|
|
500
|
+
M{O(n**2)} with M{n} the number of points). The B{C{modified}}
|
|
501
|
+
C{VW} method removes I{all} points with a triangular I{area}
|
|
502
|
+
below the tolerance in each iteration, significantly reducing
|
|
503
|
+
the run time (but producing results different from the original
|
|
504
|
+
C{VW} method).
|
|
617
505
|
'''
|
|
618
506
|
S = _Sy(points, area, radius, False, indices, **options)
|
|
619
|
-
|
|
620
507
|
if S.vwn() > 2:
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
508
|
+
if modified:
|
|
509
|
+
S.vwrm2(S.s2)
|
|
510
|
+
else:
|
|
511
|
+
S.vwrm2(0)
|
|
512
|
+
S.vwrm()
|
|
625
513
|
return S.vwr(attr)
|
|
626
514
|
|
|
627
515
|
# **) MIT License
|
|
628
516
|
#
|
|
629
|
-
# Copyright (C) 2016-
|
|
517
|
+
# Copyright (C) 2016-2025 -- mrJean1 at Gmail -- All Rights Reserved.
|
|
630
518
|
#
|
|
631
519
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
632
520
|
# copy of this software and associated documentation files (the "Software"),
|