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: enter image description here

i tried using mean, median, centroids , on.

what want every text inside or outside polygon , here: enter image description 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) 

center location

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) 

abbreviated names

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 

sorted area size

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) 

legend box

(note: longer text, you'll need increase x-axis limits well, and/or insert line breaks.)


Comments

Popular posts from this blog

networking - Vagrant-provisioned VirtualBox VM is not reachable from Ubuntu host -

c# - ASP.NET Core - There is already an object named 'AspNetRoles' in the database -

ruby on rails - ArgumentError: Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true -