Convert ggplot2 graph to plotly - legend, labels and values
This works, but you may not like how it works. The issue with the tool tip only appearing on the line... bottom line, it's a ticket in Github.
This takes the legend, title, and subtitle from the ggplot
object and adds them to the plotly
object as an image. If you resize the image, you have to refresh, to get things aligned again.
library(tidyverse)
library(plotly)
library(magick) # for the legend and title
library(ggpubr) # for extracting the ggplot legend
# load data
# didn't include it--you obviously have your data
load("./_rdata/geosData.Rdata")
From here I created your ggplot
object named rmurcia
. The only element I changed was ggplot()
, the rest is exactly as you originally designed it.
The revised ggplot()
:
rmurcia <- ggplot(data = geosmunicipios,
aes(text = paste('\nMunicipio: ', NOMBRE,
'\nRenta Hogar 2016: ',
format(Renta.media.por.hogar.2016,
big.mark=".",
decimal.mark=","),
"€",
'\nRenta Hogar 2015: ',
format(Renta.media.por.hogar.2015,
big.mark = ".",
decimal.mark=","),
"€",
sep = ""),
color = Renta.media.por.hogar.2016.quantile)) +
and the rest of your code for this part.
Next, the legend:
#----------- the legend for plotly -------------
# instead of recreating your legend or title (matching font blocks, etc)
ggLegend = get_legend(rmurcia, position = "bottom")
as_ggplot(ggLegend)
# create a temp file to hold the image
temp <- tempfile(fileext = "png")
# save plot legend as an image
ggsave(filename = temp,
plot = as_ggplot(ggLegend),
device = "png",
scale = 1,
bg = "transparent")
# now read the png and convert to raster for plotly
imgr <- image_read(temp)
# take a look at the excessive whitespace that ggplot added
image_border(image_background(imgr,
"hotpink"),
"#000080",
".1x.1") # you will have to scroll A LOT in the viewer pane
# remove this excess
(imgr <- image_trim(imgr))
# and convert to raster for plotly
imgrR <- as.raster(imgr)
# take a look to make sure it looks as expected
plot(imgrR)
# remove the tempfile
unlink(temp)
Now for the title and subtitle:
#----------- the title for plotly -------------
# literally an empty graph with the title you originally specified
forTitle <- ggplot(data = geosmunicipios) +
theme_void() +
labs(title = "Región de Murcia",
subtitle = "Renta media por hogar (2016)") +
theme(text = element_text(color = "#22211d"),
plot.title = element_text(size= 22,
hjust=0.5,
color = "#4e4d47",
margin = margin(b = -0.1,
t = 0.4,
l = 2,
unit = "cm")),
plot.subtitle = element_text(size= 17,
hjust=0.5,
color = "#4e4d47",
margin = margin(b = -0.1,
t = 0.43,
l = 2,
unit = "cm"))
)
# create a temp file to hold the image
tmp <- tempfile(fileext = "png")
# save title plot as an image
ggsave(filename = tmp,
plot = forTitle,
device = "png",
scale = 1,
bg = "transparent")
# now read the png
imgr2 <- image_read(tmp)
# take a look at the excessive whitespace that ggplot added
image_border(image_background(imgr2,
"hotpink"),
"#000080",
".1x.1") # you will have to scroll to the right
# remove this excess
(imgr2 <- image_trim(imgr2))
# and convert to raster for plotly
imgr2r <- as.raster(imgr2)
# take a look to make sure it looks as expected
plot(imgr2r)
# remove the tempfile
unlink(tmp)
I do want to caveat to say that you could use "\n" and keep the title and subtitle together as the title. However, that keeps them the same font size. I thought that was ugly, so I came up with this approach. (Where there is a will, there is a way.)
Now for the plotly
object...
#------------- now for plotly ---------------
ggplotly(rmurcia,
tooltip = "text") %>%
style(hoveron = "points+fills", # this isn't working,
# currently a ticket in Github
# this traces makes it so there is only one type of tooltip
traces = seq.int(2,
length(rmurcia$x$data))) %>%
hide_legend() %>% # we aren't going to use the plotly legend
layout( # remove the legend layout code
title = list(text = ""), # remove the title
images = list(
list(source = raster2uri(imgrR), # the legend
xref = "paper",
yref = "paper",
x = 0.32, # x placement on the grid
y = 0.03, # y placement on the grid
sizex = .4, # scale to 40% of size width
sizey = .4, # scale to 40% of size height
opacity = 1,
layer = "above"),
list(source = raster2uri(imgr2r), # the title/subtitle
xref = "paper",
yref = "paper",
x = .23, # x placement on the grid
y = 1.09, # y placement on the grid
sizex = .55, # scale to 55% of size width
sizey = .55, # scale to 55% of size height
opacity = 1,
layer = "above")
# for controlling the plot size
), margin = list(l = 4,
r = 40,
b = 65,
t = 65,
pad = 4)
)
I couldn't figure out a way to get rid of the black 'L', though I did try. You could add an image of white blocks to cover it, I suppose.
The sizes in the layout can change quite a bit, depending on the size of your plots/viewer pane. I would suggest choosing what you like best in ggplot
and then work to adjust plotly
from there. You will have to adjust the layout of the images if your ideal plot is a lot different dimensionally speaking.
As ggplot
with export pixels of 608 wide by 661 high:
As plotly
using same sized pane and the dimensions as in the code:
Almost forgot -- the tooltips:
I can tell you that the black 'L' in the plotly
object is the basis for zooming in, panning, and all that.