Lake Ōhau example
In this vignette, based on the example of Lake Ōhau, the
keep
parameter in cnt_skeleton()
and
cnt_path_guess()
is demystified. Simply put, it is
responsible for the simplification/densification of the input polygon.
That is, when keep < 1
, geometry simplification is
performed (using the geos::geos_simplify()
function).
Meanwhile, when keep > 1
, the densification algorithm is
used (through geos::geos_densify()
).
library(centerline)
lake <-
sf::st_read(
system.file("extdata/example.gpkg", package = "centerline"),
layer = "lake",
quiet = TRUE
)
The influence of keep
is best observed in the example of
generating the skeleton of a polygon. Since simplification reduces the
number of nodes in the polygon, while densification, on the contrary,
increases it. Therefore, generating skeletons using the Voronoi method
will produce more lines as the keep
parameter
increases.
skeleton_original <-
lake |>
cnt_skeleton(keep = 1)
skeleton_simplified <-
lake |>
cnt_skeleton(keep = 0.25)
skeleton_densified <-
lake |>
cnt_skeleton(keep = 1.5)
On the other hand, the difference in centerline results is negligible. Indeed, the simplified centerline is less dense than the original, while the densified one is almost identical to the original. Additionally, the simplified one ends in a slightly different location compared to the others.
cnt_original <-
lake |>
cnt_path_guess(keep = 1)
cnt_simplified <-
lake |>
cnt_path_guess(keep = 0.25)
cnt_densified <-
lake |>
cnt_path_guess(keep = 1.5)
However, when it comes to speed comparison, the simplification significantly helps to reduce the computation time. It is approximately 3-4 times faster than the estimation based on the original geometry.
Overall, I would say that it all depends on the goals of your
calculations. If you are interested in quickly labelling the polygon
along the center, it is better to use cnt_path_guess()
with
keep ≈ 0.5
to preserve the overall outline of the
centerline while significantly speeding up the calculation process.
However, if the goal is to measure the length of the polygon, then
cnt_path_guess()
with keep >= 1
may turn
out to be the best option.
bench::mark(
original = cnt_path_guess(lake, keep = 1),
simplified = cnt_path_guess(lake, keep = 0.25),
densified = cnt_path_guess(lake, keep = 1.5),
relative = TRUE,
check = FALSE,
iterations = 5L
)
#> Warning: Some expressions had a GC in every iteration; so filtering is
#> disabled.
#> # A tibble: 3 × 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 original 5.88 5.82 3.72 9.33 Inf
#> 2 simplified 1 1 21.7 1 NaN
#> 3 densified 21.0 21.2 1 31.3 Inf
Why not use %packagename% for simplification?
That’s a good question. Initially, the centerline
package was developed with rmapshaper::ms_simplify()
under
the hood by default. I really like the mapshaper
JavaScript
library and its bindings to R because it can preserve topology and keep
the overall shape (see the rmapshaper
vignette)
while dealing with extremely large geometries. However, it is not as
fast as you might imagine, creates an additional package dependency, and
there are problems with installation on Linux machines and CRAN checks.
So we decided to ditch it.
Instead, we created a geos
-based function
(centerline:::geos_ms_simplify()
) to mimic the
rmapshaper::ms_simplify()
behavior. It is not yet
accessible via export, as it is an internal function used only in
cnt_skeleton()
. It performs approximately 60 times faster
(see benchmarks) and produces similar (but not identical) outputs.
bench::mark(
rmapshaper = rmapshaper::ms_simplify(lake, keep = 0.5),
centerline = centerline:::geos_ms_simplify(lake_geos, keep = 0.5),
check = FALSE,
relative = TRUE,
iterations = 5L
)
#> # A tibble: 2 × 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 rmapshaper 80.7 80.8 1 Inf NaN
#> 2 centerline 1 1 76.7 NaN NaN
However, if you still think that the built-in simplification is mediocre, or if it produces an error, consider generating centerlines/skeletons as follows:
lake_ms_centerline <-
lake |>
rmapshaper::ms_simplify(keep = 0.5) |>
cnt_path_guess(keep = 1) # Mind the 'keep' parameter
lake_centerline <-
lake |>
cnt_path_guess(keep = 0.5)