ggrepel
ggrepel copied to clipboard
How to get the coordinates (x, xend, y, yend) of an annotate(text)
Summary
Hello everyone,
My question is not directly related to an issue with ggrepel(). But actually ggrepel address perfectly the problem I'd like to solve.
I'm working on a similar (but not so developped) function to the one of ggrepel. I'd like to avoid that an annotate("segment",...) overlaps an annotate("text",...).
Part of the code is avaible here:
list(
geom_point(data = tab_coordinates, aes(x = X[point], y = Y[point]),colour = col),
annotate("segment", x = (x_mi+dec_margin*ampli_x), xend = tab_coordinates$X[point],
y = tab_coordinates$Y[point], yend = tab_coordinates$Y[point], colour = col),
annotate("text", (x_mi + (dec_margin-.035)*ampli_x), tab_coordinates$Y[point], label = illust,
size= 10*siz, family = font, colour = col)
)
In order to overcome this issue, I had in mind to get the coordinates of the virtual box around my text zone (like x, xend, y, and yend) in order to determine this coordinates in my annotate("segment",...).
y y end
+--------------------+
| |
| annotate("text") |
| box |
| |
+--------------------+
x x end
The point as you can see on my code above is that I only entered x & y value in my annotate("text",...).
Actually I'm looking for something similar to the following code I found in order to get the extrem coordinates of the plot area of a ggplot object with the following code :
gb = ggplot_build(chart_object)
x_mi = gb$layout$panel_params[[1]]$x.range[1]
x_ma = gb$layout$panel_params[[1]]$x.range[2]
y_mi = gb$layout$panel_params[[1]]$y.range[1]
y_ma = gb$layout$panel_params[[1]]$y.range[2]
To your knowledge is there a similar code to get coordinates of a annotate("text",...) box ?
I hope that my question is clear enough for somebody to help me... Feel free to ask for more elaborate description
Version information
Here is the output from sessionInfo()
in my R session:
R version 3.6.3 (2020-02-29)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 18362)
Matrix products: default
attached base packages:
[1] grid stats graphics grDevices utils datasets methods
[8] base
other attached packages:
[1] ggrepel_0.8.2 showtext_0.7-1 showtextdb_2.0 sysfonts_0.8 raster_3.0-12
[6] geometry_0.4.5 rgeos_0.5-2 ggsn_0.5.0 stringr_1.4.0 png_0.1-7
[11] reshape2_1.4.3 plyr_1.8.6 ggplot2_3.3.0 maptools_0.9-9 mapdata_2.3.0
[16] maps_3.3.0 rgdal_1.4-4 sp_1.3-1
I'm not sure if I understand what you are looking for. Is your issue the same as https://github.com/slowkow/ggrepel/issues/35?
You might consider sharing a code snippet, a figure showing the output, and a description of your goals or issues.
Difficult to add a code, as it request to have external shape files and I don't want to copy/paste something I'm developing.
The issue is simple in the ggrepel() function, what in the code made that the segment is automaticaly adjusted depending on the label size?
Well I wrote quickly the code below :
library(ggplot2)
library(rgdal)
library(rgeos)
library(usmap)
#US map
map <- plot_usmap(regions = "states")
#Coordinates
tab_coordinates <- data.frame("Town" = c("Point 1","Point 2"), "x" = c(1000000,200000), "y" = c(-800000,-100000), stringsAsFactors = FALSE)
#Coordinates of the map margins
gb = ggplot_build(map)
x_mi = gb$layout$panel_params[[1]]$x.range[1]
x_ma = gb$layout$panel_params[[1]]$x.range[2]
y_mi = gb$layout$panel_params[[1]]$y.range[1]
y_ma = gb$layout$panel_params[[1]]$y.range[2]
ampli_x <- x_ma-x_mi
ampli_y <- y_ma-y_mi
dec_margin <- 0.075
inc <- 0.025
siz <- 1
# Indication of Point 1
map<- map +
geom_point(data = tab_coordinates, aes(x = x[1], y = y[1]), colour = 'red') +
annotate("segment", x = tab_coordinates$x[1], xend = (x_ma-dec_margin*ampli_x),
y = tab_coordinates$y[1], yend = tab_coordinates$y[1], colour = 'red') +
annotate("text", (x_ma-(dec_margin-.035)*ampli_x), tab_coordinates$y[1], label = tab_coordinates$Town[1], size= 10*siz,colour = 'red')
# Indication of Point 2
map<- map +
geom_point(data = tab_coordinates, aes(x = x[2], y = y[2]), colour = 'blue') +
annotate("segment", x = tab_coordinates$x[2], xend = (x_ma-dec_margin*ampli_x),
y = tab_coordinates$y[2], yend = tab_coordinates$y[2], colour = 'blue') +
annotate("text", (x_ma-(dec_margin-.035)*ampli_x), tab_coordinates$y[2], label = tab_coordinates$Town[2], size= 5*siz,colour = 'blue')
map
The point is that for "Point 1" text overlaps the segment that relates to the point. Is there a way to automaticaly adjust the segment to the text? In other words is there a way to get the coordinates of a the 4 points (x, xend, y, yend) of the virtual box that circle the text "Point 1" in order to determine the coordinates for my annotate("segment",...)?
You might consider reading the source code.
Dear slowkow,
Thanks for your answer. That's exactly what I want to do, but I'm still an "amateur" and don't know how to deal with this. Is there a way to be shunt?
In a previous exchange by e-mail, you point an interesting track:
The ggrepel package has internal code that tries to estimate the size of the data point and the size of the text label, so that the segment is sized appropriately.
Where is it possible to have access to this internal code? Indeed estimating the size of the text label is exactly what I want to get in order to accurately estimate to xend parameter of my annotate("segment", …) function.
You might want to look at xDetails()
, yDetails()
, grobX()
, grobY()
at:
https://stat.ethz.ch/R-manual/R-devel/library/grid/html/xDetails.html
Here is the code in ggrepel:
https://github.com/slowkow/ggrepel/blob/e2943a190c9d46ccabed9a81f34f407b852dcb99/R/geom-text-repel.R#L369-L372
I must admit that the code in ggrepel is not ideal, but it seems to work well enough. I think that a grid expert would write the code differently. Feel free to play around and discover what works for you.
Dear slowkow,
Thanks a lot for your super help, I think that it is definitely the track I was looking for! I'll play with this part of your code. I'll be back asap with news and hopefully to close the discussion!
Regarding the code of ggrepel, I'm surely not an expert, but look to all the people that use ggrepel(), most of them don't look at the core of the code (fortunatelly), they use it as a tools and it's definitely a cool & useful tool :-)!
I worked on the track, and I'm facing a new issue. Even if I used the "native" parameter for unit in the convert() functions I have a difference of unit between my initial ggplot and the coordinates obtained with convertWidth() and convertHeight().
Here is the code:
library(ggplot2)
library(rgdal)
library(rgeos)
library(usmap)
library(grid)
#US map
map <- plot_usmap(regions = "states")
#Coordinates
tab_coordinates <- data.frame("Town" = c("Point 1","Point 2"), "x" = c(1000000,200000), "y" = c(-800000,-100000), stringsAsFactors = FALSE)
#Coordinates of the map margins
gb = ggplot_build(map)
x_mi = gb$layout$panel_params[[1]]$x.range[1]
x_ma = gb$layout$panel_params[[1]]$x.range[2]
y_mi = gb$layout$panel_params[[1]]$y.range[1]
y_ma = gb$layout$panel_params[[1]]$y.range[2]
ampli_x <- x_ma-x_mi
ampli_y <- y_ma-y_mi
dec_margin <- 0.075
inc <- 0.025
siz <- 1
info_1 <-textGrob(tab_coordinates$Town[1], gp = gpar (fontsize = (10*siz) * .pt))
x1 <- convertWidth(grobX(info_1, "west"), "native", TRUE)
x2 <- convertWidth(grobX(info_1, "east"), "native", TRUE)
y1 <- convertHeight(grobY(info_1, "south"), "native", TRUE)
y2 <- convertHeight(grobY(info_1, "north"), "native", TRUE)
info_2 <-textGrob(tab_coordinates$Town[2],gp = gpar (fontsize = (1*siz) * .pt))
x3 <- convertWidth(grobX(info_2, "west"), "native", TRUE)
x4 <- convertWidth(grobX(info_2, "east"), "native", TRUE)
y3 <- convertHeight(grobY(info_2, "south"), "native", TRUE)
y4 <- convertHeight(grobY(info_2, "north"), "native", TRUE)
Here is the summary of the output:
Coordinates map info_1 info_2
1 x -2259314.9 278.5000 330.0000
2 xend 2743702.9 393.4999 341.9999
3 y -2621994.3 -322.0000 -334.0000
4 yend 891346.9 -349.9999 -337.9999
The positive point is that the font size of info_1, that is supposed to be 10 times bigger than the one of info_2, has a wider text box (114,99 > 11.999 (~ factor 10) ). The negative point is the difference of unit between coordinates of map and info_1/info_2.
Do anybody has a clue?