r - Dynamic data point label Positioning in ggmap -
i'm working ggmap package in r , relatively new geospatial data visualizations. have data frame of eleven latitude , longitude pairs plot on map, each label. here dummy data:
lat<- c(47.597157,47.656322,47.685928,47.752365,47.689297,47.628128,47.627071,47.586349,47.512684,47.571232,47.562283) lon<-c(-122.312187,-122.318039,-122.31472,-122.345345,-122.377045,-122.370117,-122.368462,-122.331734,-122.294395,-122.33606,-122.379745) labels<-c("site 1a","site 1b","site 1c","site 2a","site 3a","site 1d","site 2c","site 1e","site 2b","site 1g","site 2g") df<-data.frame(lat,lon,labels)
now use annotate
create data point labels , plot these on map;
map.data <- get_map(location = c(lon=-122.3485,lat=47.6200), maptype = 'roadmap', zoom = 11) pointlabels<-annotate("text",x=uniquereach$lon,y=c(uniquereach$lat),size=5,font=3,fontface="bold",family="helvetica",label=as.vector(uniquereach$label)) dataplot <- ggmap(map.data) + geom_point(data = uniquereach,aes(x = df$lon, y = df$lat), alpha = 1,fill="red",pch=21,size = 6) + labs(x = 'longitude', y = 'latitude')+pointlabels
this produces plot of data points
as can see, there 2 data points overlap around (-122.44,47.63), , labels overlap. can manually add shift each label point keep labels overlapping (see this post), not great technique when need produce many of these plots different sets of latitude , longitude pairs.
is there way can automatically keep data labels overlapping? realize whether labels overlap dependent on actual figure size, i'm open fixing figure size @ dimensions if need be. thank in advance insights!
edit
the following modified code using answer given sandy mupratt
# defining function draw text boxes draw.rects.modified <- function(d,...){ if(is.null(d$box.color))d$box.color <- na if(is.null(d$fill))d$fill <- "grey95" for(i in 1:nrow(d)){ with(d[i,],{ grid.rect(gp = gpar(col = box.color, fill = fill,alpha=0.7), vp = viewport(x, y, w, h, "cm", c(hjust, vjust=0.25), angle=rot)) }) } d } # defining function determine text box borders enlarge.box.modified <- function(d,...){ if(!"h"%in%names(d))stop("need have calculated height , width.") calc.borders(within(d,{ w <- 0.9*w h <- 1.1*h })) }
generating plot:
dataplot<-ggmap(map.data) + geom_point(data = df,aes(x = df$lon, y = df$lat), alpha = 1, fill = "red", pch = 21, size = 6) + labs(x = 'longitude', y = 'latitude') + geom_dl(data = df, aes(label = labels), list(dl.trans(y = y + 0.3), "boxes", cex = .8, fontface = "bold"))
this more readable plot, 1 outstanding issue. you'll note label "site 1e" begins overlap data point associated "site 1a". directlabels have way dealing labels overlapping data points belonging label?
a final question have regarding how can plot several duplicate labels using method. suppose labels data.frame same:
df$labels<-rep("test",dim(df)[1])
when use same code, directlabels removes duplicate label names:
but want each data point have label of "test". suggestions?
edit 11 jan 2016: using ggrepel
package ggplot2
v2.0.0 , ggmap
v2.6
ggrepel
works well. in code below, geom_label_repel()
shows of available parameters.
lat <- c(47.597157,47.656322,47.685928,47.752365,47.689297,47.628128,47.627071, 47.586349,47.512684,47.571232,47.562283) lon <- c(-122.312187,-122.318039,-122.31472,-122.345345,-122.377045,-122.370117, -122.368462,-122.331734,-122.294395,-122.33606,-122.379745) labels <- c("site 1a","site 1b","site 1c","site 2a","site 3a","site 1d", "site 2c","site 1e","site 2b","site 1g","site 2g") df <- data.frame(lat,lon,labels) library(ggmap) library(ggrepel) library(grid) map.data <- get_map(location = c(lon = -122.3485, lat = 47.6200), maptype = 'roadmap', zoom = 11) ggmap(map.data) + geom_point(data = df, aes(x = lon, y = lat), alpha = 1, fill = "red", pch = 21, size = 5) + labs(x = 'longitude', y = 'latitude') + geom_label_repel(data = df, aes(x = lon, y = lat, label = labels), fill = "white", box.padding = unit(.4, "lines"), label.padding = unit(.15, "lines"), segment.color = "red", segment.size = 1)
original answer updated ggplot
v2.0.0 , ggmap
v2.6
if there small number of overlapping points, using "top.bumpup" or "top.bumptwice" method direct labels package can separate them. in code below, use geom_dl()
function create , position labels.
lat <- c(47.597157,47.656322,47.685928,47.752365,47.689297,47.628128,47.627071, 47.586349,47.512684,47.571232,47.562283) lon <- c(-122.312187,-122.318039,-122.31472,-122.345345,-122.377045,-122.370117, -122.368462,-122.331734,-122.294395,-122.33606,-122.379745) labels <- c("site 1a","site 1b","site 1c","site 2a","site 3a","site 1d", "site 2c","site 1e","site 2b","site 1g","site 2g") df <- data.frame(lat,lon,labels) library(ggmap) library(directlabels) map.data <- get_map(location = c(lon = -122.3485, lat = 47.6200), maptype = 'roadmap', zoom = 11) ggmap(map.data) + geom_point(data = df, aes(x = lon, y = lat), alpha = 1, fill = "red", pch = 21, size = 6) + labs(x = 'longitude', y = 'latitude') + geom_dl(data = df, aes(label = labels), method = list(dl.trans(y = y + 0.2), "top.bumptwice", cex = .8, fontface = "bold", family = "helvetica"))
edit: adjusting underlying labels
a couple of methods spring mind, neither entirely satisfactory. don't think find solution apply situations.
adding background colour each label
bit of workaround, directlabels
has "box" function (i.e., labels placed inside box). looks 1 should able modify background fill , border colour in list in geom_dl
, can't work. instead, take 2 functions (draw.rects
, enlarge.box
) directlabels website; modify them; , combine modified functions "top.bumptwice" method.
draw.rects.modified <- function(d,...){ if(is.null(d$box.color))d$box.color <- na if(is.null(d$fill))d$fill <- "grey95" for(i in 1:nrow(d)){ with(d[i,],{ grid.rect(gp = gpar(col = box.color, fill = fill), vp = viewport(x, y, w, h, "cm", c(hjust, vjust=0.25), angle=rot)) }) } d } enlarge.box.modified <- function(d,...){ if(!"h"%in%names(d))stop("need have calculated height , width.") calc.borders(within(d,{ w <- 0.9*w h <- 1.1*h })) } boxes <- list("top.bumptwice", "calc.boxes", "enlarge.box.modified", "draw.rects.modified") ggmap(map.data) + geom_point(data = df,aes(x = lon, y = lat), alpha = 1, fill = "red", pch = 21, size = 6) + labs(x = 'longitude', y = 'latitude') + geom_dl(data = df, aes(label = labels), method = list(dl.trans(y = y + 0.3), "boxes", cex = .8, fontface = "bold"))
add outline each label
option use this method give each label outline, although not clear how work directlabels. therefore, need manual adjustment of coordinates, or search of dataframe coordinates within given threshold adjust. however, here, use pointlabel
function maptools
package position labels. no guarantee work every time, got reasonable result data. there random element built it, can run few time until reasonable result. also, note positions labels in base plot. label locations have extracted , loaded ggplot/ggmap.
lat<- c(47.597157,47.656322,47.685928,47.752365,47.689297,47.628128,47.627071,47.586349,47.512684,47.571232,47.562283) lon<-c(-122.312187,-122.318039,-122.31472,-122.345345,-122.377045,-122.370117,-122.368462,-122.331734,-122.294395,-122.33606,-122.379745) labels<-c("site 1a","site 1b","site 1c","site 2a","site 3a","site 1d","site 2c","site 1e","site 2b","site 1g","site 2g") df<-data.frame(lat,lon,labels) library(ggmap) library(maptools) # pointlabel function # map map.data <- get_map(location = c(lon=-122.3485,lat=47.6200), maptype = 'roadmap', zoom = 11) bb = t(attr(map.data, "bb")) # map's bounding box # base plot plot points , using pointlabels() position labels plot(df$lon, df$lat, pch = 20, cex = 5, col = "red", xlim = bb[c(2,4)], ylim = bb[c(1,3)]) new = pointlabel(df$lon, df$lat, df$labels, pos = 4, offset = 0.5, cex = 1) new = as.data.frame(new) new$labels = df$labels ## draw map map = ggmap(map.data) + geom_point(data = df, aes(x = lon, y = lat), alpha = 1, fill = "red", pch = 21, size = 5) + labs(x = 'longitude', y = 'latitude') ## draw label outlines theta <- seq(pi/16, 2*pi, length.out=32) xo <- diff(bb[c(2,4)])/400 yo <- diff(bb[c(1,3)])/400 for(i in theta) { map <- map + geom_text(data = new, aes_(x = new$x + .01 + cos(i) * xo, y = new$y + sin(i) * yo, label = labels), size = 3, colour = 'black', vjust = .5, hjust = .8) } # draw labels map + geom_text(data = new, aes(x = x + .01, y = y, label=labels), size = 3, colour = 'white', vjust = .5, hjust = .8)
Comments
Post a Comment