r - How to make sure text title is inside the polygon object? -
i making map plot, want put small text label inside every state. current problem text goes outside state limits, doesn't nice:
i tried using mean, median, centroids , on.
what want every text inside or outside polygon , here: (image http://www.businessinsider.com/map-what-100-is-actually-worth-in-your-state-2015-7?ir=t)
i use following code generate picture:
library(maps) library(dplyr) library(ggplot2) #data mapbase <- map_data("state.vbm") data(state.vbm.center) df <- state.vbm.center %>% as.data.frame() %>% mutate(region = unique(mapbase$region) ) %>% full_join(mapbase) #actual plotting cnames <- aggregate(cbind(long, lat) ~ region, data=df, fun=median) gmap<- ggplot()+ geom_polygon( data=df2, aes(long, lat, group = region, fill = somevalue,alpha=0.3)) + coord_fixed() + theme_void() + geom_text(data=cnames, aes( fontface=2 ,cnames$long, cnames$lat , label = "text" ), color= "black" ,size=3,check_overlap = t, position=position_jitter(width=3, height=3) ) + scale_fill_gradient(low="red",high="blue")
thanks hints!
several points consideration.
1 - optimal place annotation purposes within polygon
in ideal world, every polygon similar circle, , centre best place position text label (e.g. texas). in reality, map regions come in sorts of shapes, & may not in 1 piece (e.g. michigan). mathematical mean / median point may on edge or outside polygon (e.g. florida).
r isn't going great @ trying figure out these complications. i'd use gis software instead.
however, if use case us, state.vbm.center
dataset comes pretty set of default coordinates. file states:
state.vbm.center coordinates of state centers for annotation purposes.
let's take @ these points are:
#data mapbase <- map_data("state.vbm") data(state.vbm.center) cnames <- state.vbm.center %>% as.data.frame() %>% mutate(region = unique(mapbase$region)) #actual plotting ggplot()+ geom_polygon( data=mapbase, aes(long, lat, group = region, fill = region), alpha = 0.3) + coord_fixed() + theme_void() + geom_point(data = cnames, aes(x, y)) + scale_fill_discrete(guide = f)
that's not shabby. if need label state names, should suffice:
cnames$abb <- state.abb ggplot()+ geom_polygon( data=mapbase, aes(long, lat, group = region, fill = region), alpha = 0.3) + coord_fixed() + theme_void() + geom_text(data=cnames, aes(x, y , label = abb), color= "black", size=3, fontface = 2, hjust = 0.5, vjust = 0.5) + #central alignment scale_fill_discrete(guide = f)
2 - fitting long labels tight spaces
it's fitting short labels within map polygons, if want include more information (full name of each state, birth rate, crime rate, unemployment rate, education level, income range, population density, proportion of people voted in last election, ...), you'll start run out of space in smaller / more weirdly shaped polygons.
a dual approach can adopted @ point, keeping information within larger polygons, & placing smaller polygons separately on 1 side partial legend. states, state area part of standard datasets
package, saves trouble of calculating it:
# incorporate area information & identify small area states cnames$area <- state.area ggplot(cnames %>% mutate(region = factor(region, levels = region[order(area)])), aes(x = region, y = area)) + geom_col() + theme_classic() + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) # first 7 states (up maryland) noticeably smaller rest
pick nice empty area on map small states. decided align them vertically in 1 column @ longitude = 140, & latitude ranging 0 60:
library(tidyr) legend.states <- cnames$region[which(cnames$area <= 10577)] legend.states <- as.data.frame(legend.states) legend.states$long1 <- 140 legend.states$lat1 <- seq(0, 60, length.out = nrow(legend.states)) legend.states <- legend.states %>% mutate(long2 = long1 + 5, lat2 = lat1) %>% mutate(long3 = long2, lat3 = lat2 - 5) %>% mutate(long4 = long1, lat4 = lat3) %>% mutate(long5 = long1, lat5 = lat1) %>% gather(k, v, -legend.states) %>% mutate(order = as.integer(substring(k, nchar(k))), k = gsub("[0-9]", "", k)) %>% spread(k, v) %>% rename(region = legend.states) %>% mutate(group = mapbase$group[match(region, mapbase$region)]) %>% select(long, lat, group, order, region) %>% mutate(subregion = na) # add legend polygons original polygon dataset mapbase2 <- rbind(mapbase, legend.states)
change annotation coordinates these small states, such aligned legend box positions:
cnames2 <- left_join(cnames, legend.states %>% filter(order %in% c(1, 4)) %>% group_by(region) %>% summarise(long = mean(long) + 7, lat = mean(lat))) %>% mutate(x = coalesce(long, x), y = coalesce(lat, y), hjust = ifelse(is.na(lat), 0.5, 0)) # left alignment (hjust=0) small state text, central alignment (hjust=0.5) otherwise.
put together:
ggplot()+ geom_polygon( data=mapbase2, aes(long, lat, group = region, fill = region), alpha = 0.3) + coord_fixed() + theme_void() + geom_text(data=cnames2, aes(x, y , label = abb, hjust = hjust), size=3, fontface = 2, vjust = 0.5) + scale_fill_discrete(guide = f)
(note: longer text, you'll need increase x-axis limits well, and/or insert line breaks.)
Comments
Post a Comment