Reputation: 7654
My challenge is to add several text labels around the same point on a map. The MWE data frame puts six sports teams around New York City.
library(maps)
library(mapproj)
library(maptools)
all_states <- map_data("state") # load map outline and borders for US states
ny <- subset(all_states, region == "new york") # select only New York
nyteams <- c("Mets", "Yankees", "Knicks", "Giants", "Islanders", "Jets") # for text labels
df <- data.frame(long = rep(-73.99, times = 6), lat = rep(40.71, times =6)) # NYC coordinates for each team
df <- cbind(nyteams, df) # combine the columns to create the data frame for ggplot2
df <- cbind(df, rownum = seq(1:nrow(df))) # variable for spreading text labels by vertical latitude
It is simple to add the text labels vertically by incrementing the latitude of each point.
df$lat2 <- df$lat + (0.1*df$rownum) # to spread the text labels up the latitude
ggplot(data = df, aes(long, lat2)) +
geom_polygon(data = ny, aes(x=long, y=lat, group = group), colour="grey70", fill="white") +
coord_map("mercator") + # did not include geom_point() since text labels are sufficient
geom_text(aes(label = nyteams), size = 3)
But I worked out manually a system for placing the first team at the NYC latitude and longitude, the 2nd team just above it, the 3rd team to the right, the 4th just below it, and the 5th to the left (I can shorten names of teams to avoid over-writing Islanders, for example), etc..
df$lat2 <- df$lat + c(0, 0.1, 0.0, -0.1, 0.0, 0.2)
df$long2 <- df$long + c(0, 0.0, 0.3, 0.0, -0.5, 0.0)
ggplot(data = df, aes(long2, lat2)) +
geom_polygon(data = ny, aes(x=long, y=lat, group = group), colour="grey70", fill="white") +
coord_map("mercator") +
geom_text(aes(label = nyteams), size = 3)
Programming Question: How might R create such multiple placements of text labels without so much manual intervention?
I tried position = "jitter" and position = "dodge" to no avail.
Several other questions on SO have asked about adding multiple text annotations to a map. Limitations afflict all of them.
Can you plot a table onto a ggmap similar to annotation_custom method for non- Cartesian coordinates Dynamic position for ggplot2 objects (especially geom_text)? But directlabels package does not work on individual points https://stats.stackexchange.com/questions/16057/how-do-i-avoid-overlapping-labels-in-an-r-plot/62856#62856 FField package
Upvotes: 1
Views: 2570
Reputation: 59345
Here's one "semi"-automated approach, which places the labels in a circular pattern around the point.
ny.coords <- data.frame(long=-73.99, lat=40.71)
n <- length(nyteams)
r <- 0.3
th <- seq(0,2*(n-1)/n*pi,len=n)
coords <- data.frame(long=r*sin(th)+ny.coords$long,lat=r*cos(th)+ny.coords$lat)
ggplot(data=ny,aes(x=long,y=lat)) +
geom_polygon(data = ny, aes(group = group), colour="grey70", fill="white") +
geom_text(data=coords,aes(label = nyteams), size = 3)+
geom_point(data=ny.coords,color="red",size=3)+
coord_map("mercator",xlim=c(-75,-73),ylim=c(40,41.5))
The "semi" bit is that I picked a radius (r
) based on the scale of the map, but you could probably automate that as well.
EDIT: Response to OP's comment.
There's nothing in this approach that explicitly avoids overlaps. However, changing the line
th <- seq(0,2*(n-1)/n*pi,len=n)
to
th <- seq(0,2*(n-1)/n*pi,len=n) + pi/(2*n)
produces this:
which has the the label positions rotated a bit and can (sometimes) avoid overlaps, if there are not too many labels.
Also, you should check out the directlabels
package.
Upvotes: 2