Reputation: 233
I have created a ggplot graph, with ggmap()
and geom_sf()
, but when I try to visualize it with ggplotly()
there are some elements that are not translated.
It seems that the labels when using scale_fill_manual()
are not interpreted by plotly.
In addition, the labels must be formatted, as can be seen in the graph generated by ggplot, and modified as established in ggplot with the labels argument of scale_fill_manual()
.
The labels should be positioned on the colored boxes. Also as you can see in the graph generated with ggplot.
The frame that adds plotly seems unaesthetic to me and I would like it not to be shown.
The subtitle shown with ggplot2 cannot be found in the plotly generated graph.
Is it possible to hide the menu (download, zoom, ...) plotly from the graphics?.
Finally, in a different vein, I have tried to add information to each area of the graph, when hovering the mouse, but it does not show anything, only the value of the variable geosmunicipios$Renta.media.por.hogar.2016.quantile, which was already displayed by default, using hoverinfo()
in ggplotly()
.
It only shows the default text that I have indicated in point 7 by hovering over the lines of the polygons but not in the area of the polygons.
In short, the ggplotly graph should look as close as possible to the one generated with ggplot2.
The file for download is available at: https://www.dropbox.com/s/9nmy0uj00jhc1y4/geosmunicipios.R?dl=0
> str(geosmunicipios)
Classes ‘sf’ and 'data.frame': 45 obs. of 21 variables:
$ CODIGOINE : chr "30001" "30002" "30003" "30004" ...
$ OBJECTID : num 577 578 579 580 581 582 583 584 585 586 ...
$ INSPIREID : chr "ES.IGN.SIGLIM34143030001" "ES.IGN.SIGLIM34143030002" "ES.IGN.SIGLIM34143030003" "ES.IGN.SIGLIM34143030004" ...
$ NATCODE : chr "34143030001" "34143030002" "34143030003" "34143030004" ...
$ NAMEUNIT : chr "Abanilla" "Abarán" "Águilas" "Albudeite" ...
$ CODNUT1 : chr "ES6" "ES6" "ES6" "ES6" ...
$ CODNUT2 : chr "ES62" "ES62" "ES62" "ES62" ...
$ CODNUT3 : chr "ES620" "ES620" "ES620" "ES620" ...
$ Shape__Are : num 0 0 0 0 0 0 0 0 0 0 ...
$ Shape__Len : num 0 0 0 0 0 0 0 0 0 0 ...
$ Unidades.territoriales : chr "30001 Abanilla" "30002 Abarán" "30003 Águilas" "30004 Albudeite" ...
$ Renta.media.por.persona.2016 : num 8444 8401 8269 7680 8465 ...
$ Renta.media.por.persona.2015 : num 8274 8338 7982 7458 8305 ...
$ Renta.media.por.hogar.2016 : num 21569 23597 23222 19855 24567 ...
$ Renta.media.por.hogar.2015 : num 21017 23522 22368 19713 23875 ...
$ CPRO : int 30 30 30 30 30 30 30 30 30 30 ...
$ CMUN : int 1 2 3 4 5 6 7 8 9 10 ...
$ DC : int 1 6 2 7 0 3 9 5 8 2 ...
$ NOMBRE : chr "Abanilla" "Abarán" "Águilas" "Albudeite" ...
$ geometry :sfc_MULTIPOLYGON of length 45; first list element: List of 1
..$ :List of 1
.. ..$ : num [1:27, 1:2] -1.14 -1.12 -1.1 -1.08 -1.06 ...
..- attr(*, "class")= chr [1:3] "XY" "MULTIPOLYGON" "sfg"
$ Renta.media.por.hogar.2016.quantile: Factor w/ 4 levels "[1.98e+04,2.19e+04)",..: 1 3 2 1 4 4 3 4 3 2 ...
- attr(*, "sf_column")= chr "geometry"
- attr(*, "agr")= Factor w/ 3 levels "constant","aggregate",..: NA NA NA NA NA NA NA NA NA NA ...
..- attr(*, "names")= chr [1:20] "CODIGOINE" "OBJECTID" "INSPIREID" "NATCODE" ...
R code of the chart
library(ggplot2)
library(plotly)
dget("geosmunicipios.R")
quantile.interval = quantile(geosmunicipios$Renta.media.por.hogar.2016, probs = c(0.00, 0.25, 0.50, 0.75, 1.00))
geosmunicipios$Renta.media.por.hogar.2016.quantile = cut(geosmunicipios$Renta.media.por.hogar.2016, breaks=quantile.interval, right = FALSE, include.lowest = TRUE)
colors = c("#fee5d9","#fcae91","#fb6a4a","#de2d26")
cuartiles <- quantile(geosmunicipios$Renta.media.por.hogar.2016, probs = c(0.00, 0.25, 0.50, 0.75, 1.00))
rmurcia <- ggplot(data = geosmunicipios) +
geom_sf(
aes(
fill=Renta.media.por.hogar.2016.quantile
),
color="#FFFFFF",
size=0.5
) +
theme_void() +
scale_fill_manual(
values = colors,
labels = c(
paste("[1Q)\n", format(cuartiles[2], big.mark=".", decimal.mark=","), "€", sep=""),
paste("[2Q)\n", format(cuartiles[3], big.mark=".", decimal.mark=","), "€", sep=""),
paste("[3Q)\n", format(cuartiles[4], big.mark=".", decimal.mark=","), "€", sep=""),
paste("[4Q]\n", format(cuartiles[5], big.mark=".", decimal.mark=","), "€", sep="")
),
guide = guide_legend(
direction = "horizontal",
nrow = 1,
#title.position = "top",
label.position = "top",
label.hjust = 1,
keyheight = 0.75
)
) +
labs(
title = "Región de Murcia",
subtitle = "Renta media por hogar (2016)",
caption = "",
fill = "" # Etiqueta para la Leyenda
) +
theme(
text = element_text(color = "#22211d"),
plot.background = element_rect(fill = "#ffffff", color = NA),
panel.background = element_rect(fill = "#ffffff", color = NA),
legend.background = element_rect(fill = "#ffffff", color = NA),
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")),
plot.caption = element_text(size=12, color = "#4e4d47", margin = margin(b = 0.3, r=-99, unit = "cm") ),
#legend.position = c(0.85, 0.08)
legend.position = "bottom"
)
rmurcia
ggplotly(
rmurcia,
hoverinfo = 'text',
text = ~paste(
'</br> Municipio: ', NOMBRE,
'</br> Renta Hogar 2016: ', Renta.media.por.hogar.2016,
'</br> Renta Hogar 2015: ', Renta.media.por.hogar.2015
)
) %>%
layout(
legend = list(
orientation = "h",
xanchor = "center",
x = 0.5,
y = -0.01
)
)
Upvotes: 1
Views: 1244
Reputation: 18754
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.
Upvotes: 3