Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to get the coordinates (x, xend, y, yend) of an annotate(text) #155

Open
Facke-ito opened this issue May 4, 2020 · 9 comments
Open
Labels

Comments

@Facke-ito
Copy link

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   
@slowkow
Copy link
Owner

slowkow commented May 6, 2020

I'm not sure if I understand what you are looking for. Is your issue the same as #35?

You might consider sharing a code snippet, a figure showing the output, and a description of your goals or issues.

@Facke-ito
Copy link
Author

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?

@Facke-ito
Copy link
Author

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",...)?

@Facke-ito
Copy link
Author

1234

@slowkow
Copy link
Owner

slowkow commented May 6, 2020

You might consider reading the source code.

@Facke-ito
Copy link
Author

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.

@slowkow
Copy link
Owner

slowkow commented May 7, 2020

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:

ggrepel/R/geom-text-repel.R

Lines 369 to 372 in e2943a1

x1 <- convertWidth(grobX(tg, "west"), "native", TRUE)
x2 <- convertWidth(grobX(tg, "east"), "native", TRUE)
y1 <- convertHeight(grobY(tg, "south"), "native", TRUE)
y2 <- convertHeight(grobY(tg, "north"), "native", TRUE)

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.

@Facke-ito
Copy link
Author

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 :-)!

@Facke-ito
Copy link
Author

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants