I have a shiny app where the user can define a polygon, but I want to throw a warning if the polygon self-intersects.
I made the MWE below.
polygon=data.frame(x_coord=c(1,2.5,7.5,6.75,4), y_coord=c(5,7,6.5,0.75,0.25))
polygon=data.frame(x_coord=c(1.5,2.5,7.5,5.5,4.5), y_coord=c(3,7,6.5,3,7.5))
not_intersect <- TRUE #how to define if the polygon self-intersects?
if (not_intersect==TRUE){
P <- ggplot2::ggplot(polygon, ggplot2::aes(x=x_coord, y=y_coord)) +
ggplot2::geom_polygon(fill="#99CCFF", color="red", alpha=0.25, linewidth=1, linetype=2) +
ggplot2::geom_point(color="red", size=1.5) +
ggplot2::scale_x_continuous(limits = c(0, 10), breaks = seq(0,10)) +
ggplot2::scale_y_continuous(limits = c(0, 10), breaks = seq(0,10)) +
ggplot2::theme_light()
grDevices::pdf(file="test.pdf", height=6, width=6)
print(P)
grDevices::dev.off()
}
Essentially, if the polygon is like the first one, I want to plot it:
But if it is like the second one, I don't want to plot it:
Any idea on how to identify these 2 types of polygons? Thanks!
A valid polygon is defined by the Open Geospatial Consortium as:
We can use sf::st_is_valid()
to check for validity. We can set the reason = TRUE
parameter to check in particular for self-intersection (the third condition):
# The same polygons as in your question
polygon1 <- data.frame(x_coord = c(1, 2.5, 7.5, 6.75, 4), y_coord = c(5, 7, 6.5, 0.75, 0.25))
polygon2 <- data.frame(x_coord = c(1.5, 2.5, 7.5, 5.5, 4.5), y_coord = c(3, 7, 6.5, 3, 7.5))
library(sf)
has_self_intersection <- function(p) {
p_is_valid <- list(as.matrix(p)) |>
st_multilinestring() |>
st_cast("POLYGON") |>
st_is_valid(reason = TRUE)
any(startsWith(p_is_valid, "Self-intersection"))
}
has_self_intersection(polygon1) # FALSE
has_self_intersection(polygon2) # TRUE
Note: I have called it has_self_intersection()
rather than not_intersects
because of the advice in How to name functions that return booleans:
Steve McDonnel's popular Code Complete suggests the list that you have "is", "has", "should", or "can", but for variable names.
Your code will then be:
if (!has_self_intersection(polygon)){
# draw the plot
}