This is an interesting problem. I like the solution proposed by @etiennebr and I agree that the key part will be running `sf::st_length()`

on a polygon to get the circumference.

I am afraid though that the solution proposed does not quite generalize: the sorting of points according to `atan2`

assumes that 1) the polygon is convex and 2) that the coordinate reference system has point zero somewhere inside the circle-like polygon. These assumptions may not be always met.

Consider this polygon - it is the county Mecklenburg from the well known & much liked North Carolina shapefile that ships with `{sf}`

:

```
library(sf)
library(dplyr)
library(ggplot2)
shape <- st_read(system.file("shape/nc.shp", package="sf")) %>%
filter(CNTY_ID == 2041) %>% # Mecklenburg, as in Charlotte of Mecklenburg-Strelitz
st_transform(crs = 6543) # official planar CRS of NC
points <- st_cast(shape, "POINT") # raw points - how to interpret these??
plot(st_geometry(shape), col = "lightgrey")
plot(st_geometry(points), col = "red", pch = 4, add = T)
```

The shape is not convex, and the origin of the official North Carolina CRS is way outside of this polygon; as a consequence the ordering of points by `atan2()`

values does not produce a satisfactory polygon:

```
points$atan2 <- atan2(st_coordinates(points)[, "X"],
st_coordinates(points)[, "Y"])
polygon <- points %>%
arrange(atan2) %>%
add_row(slice_head(.)) %>%
st_combine() %>%
st_cast("POLYGON")
plot(st_geometry(polygon), col = "lightgrey")
plot(st_geometry(points), col = "red", pch = 4, add = T)
```

As an alternative I propose a different workflow: one that builds on `sf::st_convex_hull()`

function.

The `st_convex_hull()`

likes a multipoint object for its an argument, thus the `st_union()`

call.

```
hull <- points %>%
st_union() %>%
st_convex_hull()
plot(st_geometry(hull), col = "lightgrey")
plot(st_geometry(points), col = "red", pch = 4, add = T)
```

A convex hull will approximate a circle nicely.

Should the convex hull give you too great a simplification you may consider a concave hull; an approach I like is via `concaveman::concaveman()`

; it will take a points (ie. not a mutipoint) object as an argument + it has additional argument `concavity`

to guide how closely it should hew to the data (polygons with concavity less than 1 can look rather silly).

```
hull <- points %>%
concaveman::concaveman(concavity = 5/4)
plot(st_geometry(hull), col = "lightgrey")
plot(st_geometry(points), col = "red", pch = 4, add = T)
```

Regardless of the approach to find your polygon the final call should be `sf::st_length()`

on your polygon cast as a linestring to determine its circumference.

```
hull %>%
st_cast("LINESTRING") %>%
st_length()
539658.2 [US_survey_foot]
```