diff --git a/NAMESPACE b/NAMESPACE index 3c2cf0128..b8bb8598d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -270,6 +270,9 @@ S3method(st_m_range,sfc) S3method(st_make_valid,sf) S3method(st_make_valid,sfc) S3method(st_make_valid,sfg) +S3method(st_minimum_bounding_circle,sf) +S3method(st_minimum_bounding_circle,sfc) +S3method(st_minimum_bounding_circle,sfg) S3method(st_minimum_rotated_rectangle,sf) S3method(st_minimum_rotated_rectangle,sfc) S3method(st_minimum_rotated_rectangle,sfg) @@ -485,6 +488,7 @@ export(st_linestring) export(st_m_range) export(st_make_grid) export(st_make_valid) +export(st_minimum_bounding_circle) export(st_minimum_rotated_rectangle) export(st_multilinestring) export(st_multipoint) diff --git a/NEWS.md b/NEWS.md index 2e33dba55..a07eafe7e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# version 1.0-20 + +* `st_minimum_bounding_circle()` returns geometries representing the smallest circle that contains the input; #2473 + # version 1.0-19 * fix type checks in C++ GDAL area and length computation functions, anticipating GDAL 3.10.0; #2466, #2468, #2469 by @rsbivand and @rouault diff --git a/R/geom-transformers.R b/R/geom-transformers.R index 1dd4eba9d..03b50ff34 100644 --- a/R/geom-transformers.R +++ b/R/geom-transformers.R @@ -451,6 +451,34 @@ st_minimum_rotated_rectangle.sf = function(x, dTolerance, ...) { st_set_geometry(x, st_minimum_rotated_rectangle(st_geometry(x)), ...) } +#' @name geos_unary +#' @details \code{st_minimum_bounding_circle} +#' returns a geometry which represents the "minimum bounding circle", +#' the smallest circle that contains the input. +#' @export +st_minimum_bounding_circle = function(x, ...) + UseMethod("st_minimum_bounding_circle") + +#' @export +st_minimum_bounding_circle.sfg = function(x, ...) { + get_first_sfg(st_minimum_bounding_circle(st_sfc(x), ...)) +} + +#' @export +st_minimum_bounding_circle.sfc = function(x, ...) { + if (compareVersion(CPL_geos_version(), "3.8.0") > -1) { # >= + if (isTRUE(st_is_longlat(x))) + warning("st_minimum_rotated_rectangle does not work correctly for longitude/latitude data") + st_sfc(CPL_geos_op("bounding_circle", x, 0L, integer(0), + dTolerance = 0., logical(0), bOnlyEdges = as.integer(FALSE))) + } else + stop("for st_minimum_bounding_circle, GEOS version 3.8.0 or higher is required") +} + +#' @export +st_minimum_bounding_circle.sf = function(x, ...) { + st_set_geometry(x, st_minimum_bounding_circle(st_geometry(x)), ...) +} #' @name geos_unary #' @export diff --git a/man/geos_unary.Rd b/man/geos_unary.Rd index 5505e0ffe..0e1f4b8d4 100644 --- a/man/geos_unary.Rd +++ b/man/geos_unary.Rd @@ -11,6 +11,7 @@ \alias{st_triangulate_constrained} \alias{st_inscribed_circle} \alias{st_minimum_rotated_rectangle} +\alias{st_minimum_bounding_circle} \alias{st_voronoi} \alias{st_polygonize} \alias{st_line_merge} @@ -49,6 +50,8 @@ st_inscribed_circle(x, dTolerance, ...) st_minimum_rotated_rectangle(x, ...) +st_minimum_bounding_circle(x, ...) + st_voronoi( x, envelope, @@ -169,6 +172,10 @@ rectangle has width equal to the minimum diameter, and a longer length. If the convex hill of the input is degenerate (a line or point) a linestring or point is returned. +\code{st_minimum_bounding_circle} +returns a geometry which represents the "minimum bounding circle", +the smallest circle that contains the input. + \code{st_voronoi} creates voronoi tessellation. \code{st_voronoi} requires GEOS version 3.5 or above \code{st_polygonize} creates a polygon from lines that form a closed ring. In case of \code{st_polygonize}, \code{x} must be an object of class \code{LINESTRING} or \code{MULTILINESTRING}, or an \code{sfc} geometry list-column object containing these diff --git a/src/geos.cpp b/src/geos.cpp index 5846d6952..fc08a9ed3 100644 --- a/src/geos.cpp +++ b/src/geos.cpp @@ -885,6 +885,13 @@ Rcpp::List CPL_geos_op(std::string op, Rcpp::List sfc, } } else #endif +#ifdef HAVE380 + if (op == "bounding_circle") { + double r; + for (size_t i = 0; i < g.size(); i++) + out[i] = geos_ptr(chkNULL(GEOSMinimumBoundingCircle_r(hGEOSCtxt, g[i].get(), &r, NULL)), hGEOSCtxt); + } else +#endif #ifdef HAVE390 if (op == "inscribed_circle") { for (size_t i = 0; i < g.size(); i++) { @@ -906,7 +913,6 @@ Rcpp::List CPL_geos_op(std::string op, Rcpp::List sfc, return ret; } - // [[Rcpp::export]] Rcpp::List CPL_geos_voronoi(Rcpp::List sfc, Rcpp::List env, double dTolerance = 0.0, int bOnlyEdges = 1) {