I'm using the tm_text
function in the tmap
package (for R
) to draw maps and I want to force the text that is displayed to remain inside the polygon that it refers to. How do I achieve this with this function? (Some polygons are weirdly shaped and they end up having their text falling outside the polygon. The picture above illustrates an example; the 6th district has a weird shape. I would like to know if it is possible to have its label automatically placed inside the polygon as I need to plot a large number of maps.)
First of all your text label for e.g. 6 is outside the polygon since tmap's tm_text
uses the polygon centroids as position to place the label and the centroid of the corresponding polygon is inside. Here's a reproduction of the centroids (blue dots) that are used (see left plot below):
library(tidyverse)
library(sf)
library(tmap)
# shape file data from:
# https://earthworks.stanford.edu/catalog/tufts-uscongdist107th01
df <- st_read("GISPORTAL_GISOWNER01_USCONGDIST107TH01.shp") %>%
filter(STATE == "AL") %>%
distinct(CONG_DIST, .keep_all = T)
# get polygon centroids to show how tmap works
df_points_centroid <- df %>%
mutate(centroid = st_centroid(geometry)) %>%
as.data.frame() %>%
select(-geometry) %>%
st_as_sf()
tm_shape(df) +
tm_borders() +
tm_fill(col = "gray") +
tm_text("CONG_DIST") +
tm_shape(df_points_centroid) +
tm_dots(col = "blue", size = .1) +
tm_layout(frame = F)
To use label positions which are always inside the polygon you can use st_point_on_surface()
from the sf
package (see red dots and labels in the right plot):
# get points guaranteed to be within the polygon
df_points_within <- df %>%
mutate(point_within = st_point_on_surface(geometry)) %>%
as.data.frame() %>%
select(-geometry) %>%
st_as_sf()
# show old and new text locations together
tm_shape(df) +
tm_borders() +
tm_fill(col = "gray") +
tm_shape(df_points_centroid) +
tm_dots(col = "blue", size = .1) +
tm_shape(df_points_within) +
tm_dots(col = "red", size = .1) +
tm_text("CONG_DIST") +
tm_layout(frame = F)