A very fast 2D concave hull algorithm in JavaScript by Vladimir Agafonkin, wrapped in R (generates a general outline of a point set).
library(concaveman)
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
library(purrr)
library(sf)
#> Linking to GEOS 3.8.0, GDAL 3.0.4, PROJ 6.3.1
library(tmap)
data(points)
polygons <- map(unique(points$k),
~ concaveman(points[points$k %in% .,])
) %>%
map2(unique(points$k), ~ mutate(.x, k = .y)) %>%
reduce(rbind)
tm_shape(points) +
tm_dots(col = "k", size = 0.1, legend.show = FALSE) +
tm_shape(polygons) +
tm_fill(col = "k", alpha = 0.5, legend.show = FALSE) +
tm_borders() +
tm_layout(frame = FALSE)
concaveman
can be installed from CRAN:
install.packages("concaveman")
You can also install the dev version from github:
devtools::install_github("joelgombin/concaveman")
library(concaveman)
library(dplyr)
library(purrr)
library(sf)
library(tmap)
data(points)
polygons <- concaveman(points)
polygons
#> Simple feature collection with 1 feature and 0 fields
#> geometry type: POLYGON
#> dimension: XY
#> bbox: xmin: -122.0844 ymin: 37.3696 xmax: -122.0587 ymax: 37.3942
#> CRS: +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
#> # A tibble: 1 x 1
#> polygons
#> <POLYGON [°]>
#> 1 ((-122.0809 37.3736, -122.0813 37.3764, -122.0812 37.3767, -122.082 37.3772, …
polygons2 <- map(unique(points$k),
~ concaveman(points[points$k %in% .,])
) %>%
map2(unique(points$k), ~ mutate(.x, k = .y)) %>%
reduce(rbind)
tm_shape(points) +
tm_dots(col = "k", size = 0.1, legend.show = FALSE) +
tm_shape(polygons2) +
tm_fill(col = "k", alpha = 0.5, legend.show = FALSE) +
tm_borders() +
tm_layout(frame = FALSE)
#> Warning: The shape polygons2 is invalid. See sf::st_is_valid
Signature: concaveman(points, concavity = 2, lengthThreshold = 0)
points
Can be represented as a matrix of coordinates or an sf
object.concavity
is a relative measure of concavity. 1 results in a
relatively detailed shape, Infinity results in a convex hull. You
can use values lower than 1, but they can produce pretty crazy
shapes.length_threshold
: when a segment length is under this threshold,
it stops being considered for further detalization. Higher values
result in simpler shapes.The algorithm is based on ideas from the paper A New Concave Hull Algorithm and Concaveness Measure for n-dimensional Datasets, 2012 by Jin-Seo Park and Se-Jong Oh.
This implementation by Vladimir Agafonkin dramatically improves
performance over the one stated in the paper (O(rn)
, where r
is a
number of output points, to O(n log n)
) by introducing a fast k
nearest points to a segment algorithm, a modification of a depth-first
kNN R-tree search using a priority queue.