Dynamic data point label. Positioning in ggmap

I am working with the ggmap package in R and am relatively new to geospatial data visualization. I have a data frame of eleven pairs of latitude and longitude that I would like to build on a map, each with a label. Here are the 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 I use annotate to create data point labels and plot them on a 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 creates a graph of data points. plot of data points with labels

As you can see, there are two data points that overlap around (-122.44,47.63), and their labels also overlap. Now I can manually add a shift to each label so as not to overlap the labels (see this post ), but this is not a great method when I need to produce many of these graphs for different sets of latitude and longitude pairs.

Is there a way that I can automatically overlap data labels? I understand if the inscription is overlapping based on the actual size of the figure, so I can, if necessary, fix the size of the figure at certain sizes. Thank you in advance for any ideas!

EDIT

Below is a modified code using the answer received by Sandy Mupratt

 # Defining function to 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 to determine text box borders enlarge.box.modified <- function(d,...){ if(!"h"%in%names(d))stop("need to have already calculated height and width.") calc.borders(within(d,{ w <- 0.9*w h <- 1.1*h })) } 

Creating a schedule:

 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")) 

ggmap plot with labels within text boxes

This is a MORE more readable plot, but with one unresolved issue. You will notice that the label β€œSite 1E” begins to overlap the data point associated with β€œSite 1A”. Does directlabels have a way to work with labels that overlap data points that belong to another label?

The last question I have is how can I build multiple duplicate labels using this method. Suppose the labels for data.frame are the same:

 df$labels<-rep("test",dim(df)[1]) 

When I use the same code, directlabels removes duplicate label names: enter image description here

But I want each data point to have a label "test". Any suggestions?

+6
source share
1 answer

Edit January 11th, 2016: using ggrepel package with ggplot2 v2.0.0 and ggmap v2.6

ggrepel works well. The geom_label_repel() code below shows some of the available options.

 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) 

enter image description here



Original answer, but updated for ggplot v2.0.0 and ggmap v2.6

If there is only a small number of overlapping points, then using the top.bumpup or top.bumptwice method from the direct label package can separate them. In the code below, I use the geom_dl() function to create and place 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")) 

enter image description here

Edit: setting for base tags

Several spring methods, but none of them are completely satisfactory. But I do not think that you will find a solution that will apply to all situations.

Adding a background color for each label
This is a bit of a workaround, but directlabels has a "box" function (ie. Labels are placed inside the field). It looks like you have the option to change the background fill and border color in the list in geom_dl , but I can't get it to work. Instead, I take two functions ( draw.rects and enlarge.box ) from the directlabels website; modify them; and combine the changed functions with the "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 to have already calculated height and 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")) 

enter image description here

Add a schema for each label
Another option is to use this method to give each shortcut an outline, although it is not immediately clear how it will work with directlabels. Therefore, for this you will need to manually adjust the coordinates or search for a data frame for coordinates that are in a given threshold, then adjust. However, here I use the pointLabel function from the maptools package to place labels. There is no guarantee that it will work every time, but I got a reasonable result with your data. A random element is built into it, so you can run it several times until you get a reasonable result. Also note that it places labels in the base chart. Then the tags should be retrieved and loaded into 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 # Get map map.data <- get_map(location = c(lon=-122.3485,lat=47.6200), maptype = 'roadmap', zoom = 11) bb = t(attr(map.data, "bb")) # the map bounding box # Base plot to plot points and using pointLabels() to 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 the 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 the 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 the labels map + geom_text(data = new, aes(x = x + .01, y = y, label=labels), size = 3, colour = 'white', vjust = .5, hjust = .8) 

enter image description here

+5
source

Source: https://habr.com/ru/post/987002/


All Articles