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.