Skip to content

Commit 7789658

Browse files
authored
Refactor simplification methods (#1019)
* Initial refactor * Add BinarySearchSimplification * More adjustments * Fix docstring * More adjustments * More adjustments * More adjustments * More adjustments * More adjustments * Formatting * Minor adjustments
1 parent 1d8e730 commit 7789658

File tree

7 files changed

+193
-208
lines changed

7 files changed

+193
-208
lines changed

docs/src/algorithms/simplification.md

Lines changed: 72 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -7,86 +7,103 @@ import CairoMakie as Mke # hide
77

88
```@docs
99
simplify
10-
decimate
1110
SimplificationMethod
1211
```
1312

14-
## Douglas-Peucker
13+
## SelingerSimplification
1514

1615
```@docs
17-
DouglasPeucker
16+
SelingerSimplification
1817
```
1918

2019
```@example simplification
21-
# polygonal area
22-
polyarea = PolyArea([(0.22926679, 0.47329807), (0.23094065, 0.44913536), (0.2569517, 0.38217533),
23-
(0.3072999, 0.272418), (0.34814754, 0.18421611), (0.37949452, 0.11756973),
24-
(0.4013409, 0.07247882), (0.41368666, 0.048943404), (0.42597583, 0.031655528),
25-
(0.4382084, 0.0206152), (0.45038435, 0.015822414), (0.4625037, 0.017277176),
26-
(0.47175184, 0.02439156), (0.47812873, 0.03716557), (0.4816344, 0.055599205),
27-
(0.48226887, 0.07969247), (0.48172843, 0.10446181), (0.4800131, 0.12990724),
28-
(0.47712287, 0.15602873), (0.47305775, 0.18282633), (0.47093934, 0.20558843),
29-
(0.47076762, 0.22431506), (0.47254258, 0.23900622), (0.47626427, 0.24966191),
30-
(0.47768936, 0.25845313), (0.47681788, 0.26537988), (0.4736498, 0.27044216),
31-
(0.46818516, 0.27363995), (0.4613889, 0.27232954), (0.45326096, 0.2665109),
32-
(0.44380143, 0.256184), (0.43301025, 0.24134888), (0.4246466, 0.22978415),
33-
(0.41871038, 0.22148979), (0.4152017, 0.21646582), (0.4141205, 0.21471222),
34-
(0.41227448, 0.21589448), (0.40966362, 0.22001258), (0.40628797, 0.22706655),
35-
(0.40214747, 0.23705636), (0.40200475, 0.24653101), (0.40585983, 0.25549048),
36-
(0.41371268, 0.2639348), (0.4255633, 0.2718639), (0.4378565, 0.28495985),
37-
(0.4505922, 0.30322257), (0.46377045, 0.32665208), (0.47739124, 0.35524836),
38-
(0.5046394, 0.36442512), (0.5455148, 0.35418236), (0.60001767, 0.32452005),
39-
(0.66814786, 0.27543822), (0.7186763, 0.24664374), (0.75160307, 0.23813659),
40-
(0.76692814, 0.2499168), (0.7646515, 0.28198436), (0.7769703, 0.29925033),
41-
(0.8038847, 0.3017147), (0.84539455, 0.28937748), (0.9015, 0.26223865),
42-
(0.94408435, 0.24899776), (0.9731477, 0.24965483), (0.98869, 0.26420987),
43-
(0.9907113, 0.29266283), (0.9849871, 0.31338844), (0.97151726, 0.32638666),
44-
(0.950302, 0.3316575), (0.9213412, 0.32920095), (0.8798396, 0.34078467),
45-
(0.8257972, 0.36640862), (0.7592141, 0.40607283), (0.6800901, 0.4597773),
46-
(0.6450007, 0.49104902), (0.6539457, 0.49988794), (0.7069251, 0.48629412),
47-
(0.803939, 0.45026752), (0.877913, 0.4226481), (0.9288472, 0.40343583),
48-
(0.9567415, 0.39263073), (0.961596, 0.39023277), (0.9419039, 0.40523484),
49-
(0.89766514, 0.43763688), (0.8288798, 0.48743892), (0.7355478, 0.55464095),
50-
(0.6655121, 0.60063523), (0.6187727, 0.6254217), (0.5953296, 0.62900037),
51-
(0.5951828, 0.6113712), (0.57516366, 0.60261106), (0.53527224, 0.6027198),
52-
(0.4755085, 0.6116975), (0.3958725, 0.6295441), (0.33913234, 0.6398651),
53-
(0.30528808, 0.6426605), (0.2943397, 0.6379303), (0.30628717, 0.6256744),
54-
(0.32149008, 0.6093727), (0.33994842, 0.5890249), (0.36166218, 0.5646312),
55-
(0.38663134, 0.5361916), (0.3919681, 0.520893), (0.3776725, 0.5187355),
56-
(0.34374446, 0.52971905), (0.29018405, 0.5538437), (0.25439468, 0.5678829),
57-
(0.2363764, 0.5718367), (0.23612918, 0.56570506), (0.25365302, 0.549488),
58-
(0.2733971, 0.5246488), (0.29536137, 0.49118724), (0.3195459, 0.4491035),
59-
(0.34595063, 0.39839754), (0.3647463, 0.3590396), (0.37593287, 0.33102974),
60-
(0.37951034, 0.31436795), (0.37547874, 0.30905423), (0.36070493, 0.3204269),
61-
(0.33518887, 0.348486), (0.29893062, 0.3932315), (0.25193012, 0.45466346)])
20+
poly = PolyArea([(0.22926679, 0.47329807), (0.23094065, 0.44913536), (0.2569517, 0.38217533),
21+
(0.3072999, 0.272418), (0.34814754, 0.18421611), (0.37949452, 0.11756973),
22+
(0.4013409, 0.07247882), (0.41368666, 0.048943404), (0.42597583, 0.031655528),
23+
(0.4382084, 0.0206152), (0.45038435, 0.015822414), (0.4625037, 0.017277176),
24+
(0.47175184, 0.02439156), (0.47812873, 0.03716557), (0.4816344, 0.055599205),
25+
(0.48226887, 0.07969247), (0.48172843, 0.10446181), (0.4800131, 0.12990724),
26+
(0.47712287, 0.15602873), (0.47305775, 0.18282633), (0.47093934, 0.20558843),
27+
(0.47076762, 0.22431506), (0.47254258, 0.23900622), (0.47626427, 0.24966191),
28+
(0.47768936, 0.25845313), (0.47681788, 0.26537988), (0.4736498, 0.27044216),
29+
(0.46818516, 0.27363995), (0.4613889, 0.27232954), (0.45326096, 0.2665109),
30+
(0.44380143, 0.256184), (0.43301025, 0.24134888), (0.4246466, 0.22978415),
31+
(0.41871038, 0.22148979), (0.4152017, 0.21646582), (0.4141205, 0.21471222),
32+
(0.41227448, 0.21589448), (0.40966362, 0.22001258), (0.40628797, 0.22706655),
33+
(0.40214747, 0.23705636), (0.40200475, 0.24653101), (0.40585983, 0.25549048),
34+
(0.41371268, 0.2639348), (0.4255633, 0.2718639), (0.4378565, 0.28495985),
35+
(0.4505922, 0.30322257), (0.46377045, 0.32665208), (0.47739124, 0.35524836),
36+
(0.5046394, 0.36442512), (0.5455148, 0.35418236), (0.60001767, 0.32452005),
37+
(0.66814786, 0.27543822), (0.7186763, 0.24664374), (0.75160307, 0.23813659),
38+
(0.76692814, 0.2499168), (0.7646515, 0.28198436), (0.7769703, 0.29925033),
39+
(0.8038847, 0.3017147), (0.84539455, 0.28937748), (0.9015, 0.26223865),
40+
(0.94408435, 0.24899776), (0.9731477, 0.24965483), (0.98869, 0.26420987),
41+
(0.9907113, 0.29266283), (0.9849871, 0.31338844), (0.97151726, 0.32638666),
42+
(0.950302, 0.3316575), (0.9213412, 0.32920095), (0.8798396, 0.34078467),
43+
(0.8257972, 0.36640862), (0.7592141, 0.40607283), (0.6800901, 0.4597773),
44+
(0.6450007, 0.49104902), (0.6539457, 0.49988794), (0.7069251, 0.48629412),
45+
(0.803939, 0.45026752), (0.877913, 0.4226481), (0.9288472, 0.40343583),
46+
(0.9567415, 0.39263073), (0.961596, 0.39023277), (0.9419039, 0.40523484),
47+
(0.89766514, 0.43763688), (0.8288798, 0.48743892), (0.7355478, 0.55464095),
48+
(0.6655121, 0.60063523), (0.6187727, 0.6254217), (0.5953296, 0.62900037),
49+
(0.5951828, 0.6113712), (0.57516366, 0.60261106), (0.53527224, 0.6027198),
50+
(0.4755085, 0.6116975), (0.3958725, 0.6295441), (0.33913234, 0.6398651),
51+
(0.30528808, 0.6426605), (0.2943397, 0.6379303), (0.30628717, 0.6256744),
52+
(0.32149008, 0.6093727), (0.33994842, 0.5890249), (0.36166218, 0.5646312),
53+
(0.38663134, 0.5361916), (0.3919681, 0.520893), (0.3776725, 0.5187355),
54+
(0.34374446, 0.52971905), (0.29018405, 0.5538437), (0.25439468, 0.5678829),
55+
(0.2363764, 0.5718367), (0.23612918, 0.56570506), (0.25365302, 0.549488),
56+
(0.2733971, 0.5246488), (0.29536137, 0.49118724), (0.3195459, 0.4491035),
57+
(0.34595063, 0.39839754), (0.3647463, 0.3590396), (0.37593287, 0.33102974),
58+
(0.37951034, 0.31436795), (0.37547874, 0.30905423), (0.36070493, 0.3204269),
59+
(0.33518887, 0.348486), (0.29893062, 0.3932315), (0.25193012, 0.45466346)])
6260
63-
simp1 = simplify(polyarea, DouglasPeucker(0.01))
64-
simp2 = simplify(polyarea, DouglasPeucker(0.05))
65-
simp3 = simplify(polyarea, DouglasPeucker(0.10))
61+
simp1 = simplify(poly, SelingerSimplification(0.01))
62+
simp2 = simplify(poly, SelingerSimplification(0.05))
63+
simp3 = simplify(poly, SelingerSimplification(0.10))
6664
6765
fig = Mke.Figure(size = (800, 800))
68-
viz(fig[1,1], polyarea)
66+
viz(fig[1,1], poly)
6967
viz(fig[1,2], simp1)
7068
viz(fig[2,1], simp2)
7169
viz(fig[2,2], simp3)
7270
fig
7371
```
7472

75-
## Selinger
73+
## DouglasPeuckerSimplification
7674

7775
```@docs
78-
Selinger
76+
DouglasPeuckerSimplification
7977
```
8078

8179
```@example simplification
82-
simp1 = simplify(polyarea, Selinger(0.01))
83-
simp2 = simplify(polyarea, Selinger(0.05))
84-
simp3 = simplify(polyarea, Selinger(0.10))
80+
simp1 = simplify(poly, DouglasPeuckerSimplification(0.01))
81+
simp2 = simplify(poly, DouglasPeuckerSimplification(0.05))
82+
simp3 = simplify(poly, DouglasPeuckerSimplification(0.10))
8583
8684
fig = Mke.Figure(size = (800, 800))
87-
viz(fig[1,1], polyarea)
85+
viz(fig[1,1], poly)
8886
viz(fig[1,2], simp1)
8987
viz(fig[2,1], simp2)
9088
viz(fig[2,2], simp3)
9189
fig
92-
```
90+
```
91+
92+
## MinMaxSimplification
93+
94+
```@docs
95+
MinMaxSimplification
96+
```
97+
98+
```@example simplification
99+
simp1 = simplify(poly, MinMaxSimplification(DouglasPeuckerSimplification, max=20))
100+
simp2 = simplify(poly, MinMaxSimplification(DouglasPeuckerSimplification, max=10))
101+
simp3 = simplify(poly, MinMaxSimplification(DouglasPeuckerSimplification, max=5))
102+
103+
fig = Mke.Figure(size = (800, 800))
104+
viz(fig[1,1], poly)
105+
viz(fig[1,2], simp1)
106+
viz(fig[2,1], simp2)
107+
viz(fig[2,2], simp3)
108+
fig
109+
```

src/Meshes.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -457,10 +457,10 @@ export
457457

458458
# simplification
459459
SimplificationMethod,
460-
DouglasPeucker,
461-
Selinger,
460+
SelingerSimplification,
461+
DouglasPeuckerSimplification,
462+
MinMaxSimplification,
462463
simplify,
463-
decimate,
464464

465465
# bounding boxes
466466
boundingbox,

src/simplification.jl

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,11 @@ abstract type SimplificationMethod end
1212
"""
1313
simplify(object, method)
1414
15-
Simplify `object` with given `method`.
16-
17-
See also [`decimate`](@ref).
15+
Simplify geometric `object` with given `method`.
1816
"""
1917
function simplify end
2018

21-
simplify(box::Box, method::SimplificationMethod) = _simplify(box, Val(embeddim(box)), method)
22-
23-
_simplify(box::Box, ::Val{2}, method::SimplificationMethod) = PolyArea(simplify(boundary(box), method))
19+
simplify(box::Box{𝔼{2}}, method::SimplificationMethod) = PolyArea(simplify(boundary(box), method))
2420

2521
simplify(polygon::Polygon, method::SimplificationMethod) = PolyArea([simplify(ring, method) for ring in rings(polygon)])
2622

@@ -32,22 +28,6 @@ simplify(domain::Domain, method::SimplificationMethod) = GeometrySet([simplify(e
3228
# IMPLEMENTATIONS
3329
# ----------------
3430

35-
include("simplification/douglaspeucker.jl")
3631
include("simplification/selinger.jl")
37-
38-
# ----------
39-
# UTILITIES
40-
# ----------
41-
42-
"""
43-
decimate(object, [ϵ]; min=3, max=typemax(Int), maxiter=10)
44-
45-
Simplify `object` with an appropriate simplification method
46-
and deviation tolerance `ϵ`.
47-
48-
If the tolerance `ϵ` is not provided, perform binary search until
49-
the number of vertices is between `min` and `max` or until the
50-
number of iterations reaches a maximum `maxiter`.
51-
"""
52-
decimate(object, ϵ=nothing; min=3, max=typemax(Int), maxiter=10) =
53-
simplify(object, DouglasPeucker(ϵ, min=min, max=max, maxiter=maxiter))
32+
include("simplification/douglaspeucker.jl")
33+
include("simplification/minmax.jl")

src/simplification/douglaspeucker.jl

Lines changed: 16 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -3,84 +3,34 @@
33
# ------------------------------------------------------------------
44

55
"""
6-
DouglasPeucker([ϵ]; min=3, max=typemax(Int), maxiter=10)
6+
DouglasPeuckerSimplification(τ)
77
8-
Simplify geometries with Douglas-Peucker algorithm. The higher
9-
is the tolerance `ϵ`, the more aggressive is the simplification.
8+
Douglas-Peucker's simplification algorithm with tolerance `τ` in length units
9+
(default to meter).
1010
11-
If the tolerance `ϵ` is not provided, perform binary search until
12-
the number of vertices is between `min` and `max` or until the
13-
number of iterations reaches a maximum `maxiter`.
11+
The higher is the tolerance, the more aggressive is the simplification.
1412
1513
## References
1614
1715
* Douglas, D. and Peucker, T. 1973. [Algorithms for the Reduction of
1816
the Number of Points Required to Represent a Digitized Line or its
1917
Caricature](https://www.sciencedirect.com/science/article/abs/pii/0167839691900198)
2018
"""
21-
struct DouglasPeucker{T} <: SimplificationMethod
22-
ϵ::T
23-
min::Int
24-
max::Int
25-
maxiter::Int
19+
struct DouglasPeuckerSimplification{ℒ<:Len} <: SimplificationMethod
20+
τ::
21+
DouglasPeuckerSimplification::ℒ) where {ℒ<:Len} = new{float(ℒ)}(τ)
2622
end
2723

28-
DouglasPeucker=nothing; min=3, max=typemax(Int), maxiter=10) = DouglasPeucker((ϵ), min, max, maxiter)
24+
DouglasPeuckerSimplification(τ) = DouglasPeuckerSimplification(addunit(τ, u"m"))
2925

30-
::Nothing) = ϵ
31-
::Number) = (addunit(ϵ, u"m"))
32-
::Len) = float(ϵ)
33-
34-
function simplify(chain::Chain, method::DouglasPeucker)
35-
v = if isnothing(method.ϵ)
36-
# perform binary search with other parameters
37-
βsimplify(vertices(chain), method.min, method.max, method.maxiter)
38-
else
39-
# perform Douglas-Peucker ϵ-simplification
40-
ϵsimplify(vertices(chain), method.ϵ)
41-
end |> collect
42-
isclosed(chain) ? Ring(v) : Rope(v)
43-
end
44-
45-
# simplification by means of binary search
46-
function βsimplify(v::AbstractVector{P}, min, max, maxiter) where {P<:Point}
47-
i = 0
48-
u = v
49-
n = length(u)
50-
a = zero(lentype(P))
51-
b = initeps(u)
52-
while !(min n max) && i < maxiter
53-
# midpoint candidate
54-
ϵ = (a + b) / 2
55-
56-
# evaluate at midpoint
57-
u = ϵsimplify(v, ϵ)
58-
n = length(u)
59-
60-
# binary search
61-
n < min && (b = ϵ)
62-
n > max && (a = ϵ)
63-
64-
i += 1
65-
end
66-
67-
u
68-
end
69-
70-
# initial ϵ guess for a given chain
71-
function initeps(v::AbstractVector{P}) where {P<:Point}
72-
n = length(v)
73-
ϵ = typemax(lentype(P))
74-
l = Line(first(v), last(v))
75-
d = [evaluate(Euclidean(), v[i], l) for i in 2:(n - 1)]
76-
ϵ = quantile(d, 0.25)
77-
2ϵ
26+
function simplify(chain::Chain, method::DouglasPeuckerSimplification)
27+
verts = _douglaspeucker(vertices(chain), method.τ) |> collect
28+
isclosed(chain) ? Ring(verts) : Rope(verts)
7829
end
7930

8031
# simplify chain assuming it is open
81-
function ϵsimplify(v::AbstractVector{P}, ϵ) where {P<:Point}
82-
# find vertex with maximum distance
83-
# to reference line
32+
function _douglaspeucker(v::AbstractVector{P}, τ) where {P<:Point}
33+
# find vertex with maximum distance to reference line
8434
l = Line(first(v), last(v))
8535
imax, dmax = 0, zero(lentype(P))
8636
for i in 2:(length(v) - 1)
@@ -91,11 +41,11 @@ function ϵsimplify(v::AbstractVector{P}, ϵ) where {P<:Point}
9141
end
9242
end
9343

94-
if dmax < ϵ
44+
if dmax < τ
9545
[first(v), last(v)]
9646
else
97-
v₁ = ϵsimplify(v[begin:imax], ϵ)
98-
v₂ = ϵsimplify(v[imax:end], ϵ)
47+
v₁ = _douglaspeucker(v[begin:imax], τ)
48+
v₂ = _douglaspeucker(v[imax:end], τ)
9949
[v₁[begin:(end - 1)]; v₂]
10050
end
10151
end

src/simplification/minmax.jl

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# ------------------------------------------------------------------
2+
# Licensed under the MIT License. See LICENSE in the project root.
3+
# ------------------------------------------------------------------
4+
5+
"""
6+
MinMaxSimplification(method; min=3, max=typemax(Int), maxiter=10)
7+
8+
Simplify geometries with binary search algorithm and a parent simplification `method`.
9+
10+
The simplification is performed until the number of vertices is in the `[min, max]`
11+
range or until a maximum number of iterations `maxiter` is reached.
12+
"""
13+
struct MinMaxSimplification{M} <: SimplificationMethod
14+
method::M
15+
min::Int
16+
max::Int
17+
maxiter::Int
18+
end
19+
20+
MinMaxSimplification(method; min=3, max=typemax(Int), maxiter=10) = MinMaxSimplification(method, min, max, maxiter)
21+
22+
function simplify(c::Chain, m::MinMaxSimplification)
23+
i = 0
24+
s = c
25+
n = nvertices(c)
26+
a, b = _initrange(c)
27+
while !(m.min n m.max) && i < m.maxiter
28+
# midpoint candidate
29+
τ = (a + b) / 2
30+
31+
# evaluate at midpoint
32+
s = simplify(c, m.method(τ))
33+
n = nvertices(s)
34+
35+
# binary search
36+
n < m.min && (b = τ)
37+
n > m.max && (a = τ)
38+
39+
i += 1
40+
end
41+
42+
s
43+
end
44+
45+
# initial range for binary search
46+
function _initrange(c)
47+
v = vertices(c)
48+
n = length(v)
49+
l = Line(first(v), last(v))
50+
d = [evaluate(Euclidean(), v[i], l) for i in 2:(n - 1)]
51+
z = zero(lentype(c))
52+
τ = quantile(d, 0.25)
53+
(z, 2τ)
54+
end

0 commit comments

Comments
 (0)