This is an extension to Improve centering county names ggplot & maps and ggplot centered names on a map. It is not only a theoretical problem, I came across that particular case on answering How merge specific states together by group with one label in ggplot2 in R?. Here, I found that there is an L-shaped "grid" which led to a midpoint position outside the polygon. Also, see Henrik's linked thread Calculate Centroid WITHIN / INSIDE a SpatialPolygon. .
I wondered if there might be ways to force the label into the polygon, for a "more intuitive" midpoint. "Intuitively" means probably "the point inside the polygon furthest away from any boundary" (an answer to a related thread suggested a function to calculate this, but this function doesn't seem to give a different result than rgeos::gCentroid
in my example).
Any suggestion should be fully automated, ideally be applicable to any (ir-)regular polygon and also be independent on the coordinate projection (i.e., the coordinates aspect ratio should not matter) correction: Ideally it should depend on the coordinate projection, as this might move the text to an awkward position to the border. Thus, the ideal solution should probably calculate the label position at drawing time.
Very related would be https://gis.stackexchange.com/questions/29278/finding-point-in-country-furthest-from-boundary and https://mathoverflow.net/questions/161494/get-a-point-in-polygon-maximize-the-distance-from-borders, but I have no clue how to implement this in R / grid / ggplot2.
suppressMessages({library(ggh4x)
library(sf)
library(dplyr)
library(patchwork)
})
poly_foo <- data.frame(x = c(0:1, rep(2,4), rep(1.5,3), 0), y = c(rep(0,3), 1:3, 3:1, 1))
p <- ggplot(poly_foo, aes(x, y) ) +
geom_polygon(color = "black", fill = NA)
p1 <- p +
stat_midpoint(aes(label = "ggh4x::stat_midpoint\nNot ideal"), geom = "text")
## Convert to simple feature object for geom_sf
point_sf <- st_as_sf(poly_foo, coords = c("x", "y"))
poly_sf <-
point_sf %>%
summarise(geometry = st_combine(geometry)) %>%
st_cast("POLYGON") %>%
mutate(label = "sf::st_point_on_surface\nNot ideal")
p2 <- ggplot(poly_sf) +
geom_sf() +
geom_sf_text(aes(label = label))
## convert to spatial object (sp) for rgeos::gCentroid
xy_lab <- rgeos::gCentroid(as_Spatial(poly_sf))@coords
p3 <- ggplot(poly_sf) +
geom_sf() +
annotate(geom = "text", label = "rgeos::gCentroid\nNot ideal",
x = xy_lab[1], y = xy_lab[2])
p4 <- p +
annotate(geom = "text", x = 1.7, y = .5, label = "more\nintuitive\n\'midpoint\'")
## it should ideally change with different coordinates
p5 <- p + coord_fixed() +
annotate(geom = "text", x = 1, y = .5, label = "more\nintuitive\n\'midpoint\'\ncoordinates changed")
p1 + p2 + p3 + plot_annotation(title = "Example problem")
## Desired output
p4 + p5 + plot_annotation(title = "Desired behaviour")
Created on 2022-06-26 by the reprex package (v2.0.1)