#Copyright © 2016 RTE Réseau de transport d’électricité
#' Plot an interactive legend for time series plots
#'
#' These functions create a nice looking legend that displays values when the user
#' hovers a time series produced with plot this package. By
#' default, the different functions already output a legend. This function
#' is mostly useful to share a unique legend between two or more time series plots.
#'
#' @param labels vector containing the names of the times series
#' @param colors vector of colors. It must have the same length as parameter
#' \code{labels}.
#' @param types "line" or "area" or a vector with same length as \code{labels}
#' containing these two values.
#' @inheritParams prodStack
#'
#' @details
#' Thes functions can be used to create a legend shared by multiple plots
#' in a Shiny application or an interactive document created with Rmarkdown.
#' For instance, let assume one wants to display four productions stacks in a 2x2
#' layout and have a unique legend below them in a Rmarkdown document. To do so,
#' one can use the following chunck code:
#'
#' \preformatted{
#' ```{R, echo = FALSE}
#' library(manipulateWidget)
#'
#' combineWidgets(
#' prodStack(mydata, areas = "fr",
#' main = "Production stack in France", unit = "GWh",
#' legend = FALSE, legendId = 1, height = "100\%", width = "100\%"),
#' prodStack(mydata, areas = "de",
#' main = "Production stack in Germany", unit = "GWh",
#' legend = FALSE, legendId = 1, height = "100\%", width = "100\%"),
#' prodStack(mydata, areas = "es",
#' main = "Production stack in Spain", unit = "GWh",
#' legend = FALSE, legendId = 1, height = "100\%", width = "100\%"),
#' prodStack(mydata, areas = "be",
#' main = "Production stack in Belgium", unit = "GWh",
#' legend = FALSE, legendId = 1, height = "100\%", width = "100\%"),
#' footer = prodStackLegend(legendId = 1)
#' )
#' ```
#' }
#'
#' @export
tsLegend <- function(labels, colors, types = "line", legendItemsPerRow = 5, legendId = "") {
legendItems <- mapply(.tsLegendItem,
label = labels,
color = colors,
type = types,
legendId = legendId,
SIMPLIFY = FALSE,
USE.NAMES = FALSE)
nbRows <- ceiling(length(legendItems) / legendItemsPerRow)
legendItems <- rev(legendItems)
legendRows <- list()
for (i in 1:nbRows) {
j <- ((i - 1) * legendItemsPerRow + 1):(i * legendItemsPerRow)
legendRows[[i]] <- do.call(fillRow, legendItems[j])
}
legendItems <- tags$div(
fillRow(do.call(fillCol, legendRows), height = i * 20),
style = sprintf("padding-left: 100px; height:%spx", i * 20)
)
tags$div(
style=sprintf("position:relative;height:%spx", max(i * 20 + 20, 40)),
tags$div(
style = "position:absolute;top:0;bottom:0;width:100px;
",
tags$div(
style="text-align:center;font-weight:bold;font-size:16px",
id = paste0("date", legendId)
)
),
tags$br(),
legendItems
)
}
.style <- function(...) {
values <- list(...)
keys <- names(values)
keyval <- paste(keys, values, sep = ":")
paste(keyval, collapse = ";")
}
.tsLegendItem <- function(label, color, legendId, type = "line") {
h <- "16px"
if (type == "area") {
c <- col2rgb(color)
o <- (c[1] * 299 + c[2] * 587 + c[3] * 114) / 1000
txtCol <- ifelse(o > 170, "black", "white")
content <- tags$div(
style = .style(color = txtCol, "background-color" = color,
position= "absolute", left = "2px", right = "4px", height=h),
tags$div(
style = .style(position="absolute", left="4px", padding="0 2px"),
class = "leglabel",
label),
tags$div(
style= .style(position="absolute", right="0px", padding="0 2px 0 4px",
"background-color"=color, "font-weight"="bold",
height="16px"),
id = paste0(label, legendId),
class = "legvalue",
""
)
)
} else if (type == "line") {
content <- tags$div(
style = .style(color = color,
width = "100%", height=h),
tags$div(
style = .style(position="absolute", "background-color"=color,
left="4px", right="4px", top ="8px", bottom="8px"),
""),
tags$div(
style = .style(position="absolute", left="4px", padding="0 4px 0 2px",
"background-color"="white"),
class = "leglabel",
label),
tags$div(
style= .style(position="absolute", right="4px", padding="0 2px 0 4px",
"background-color"="white", "font-weight"="bold",
height="16px"),
id = paste0(label, legendId),
class = "legvalue",
""
)
)
}
tags$div(
style = .style(width = "100%", height = h, padding = "0 4px", "font-size"= "12px"),
content
)
}
JS_updateLegend <- function(legendId, timeStep = "hourly", language = "en") {
# Function that transform a timestamp ta a date label
if(language == "fr"){
timeToLab <- switch(
timeStep,
hourly = "var date = new Date(x);
return date.toLocaleDateString('fr-FR', { weekday: 'short', month: 'short', day: 'numeric', hour : '2-digit', minute:'2-digit', timeZone : 'UTC' })",
daily = "var date = new Date(x); return date.toLocaleDateString('fr-FR', { weekday: 'short', month: 'short', day: 'numeric', timeZone : 'UTC' })",
weekly = "var date = new Date(x); return date.toLocaleDateString('fr-FR', { weekday: 'short', month: 'short', day: 'numeric', timeZone : 'UTC' })",
monthly = "var date = new Date(x); return date.toLocaleDateString('fr-FR', {month: 'long', year: 'numeric', timeZone : 'UTC' })",
annual = "var date = new Date(x); return date.toLocaleDateString('fr-FR', {year: 'numeric', timeZone : 'UTC' })",
"return x"
)
} else {
timeToLab <- switch(
timeStep,
hourly = "var date = new Date(x);
var day = date.toUTCString().slice(0, 11);
var h = date.toUTCString().slice(17, 22);
return day + '<br/>' + h;",
daily = "var date = new Date(x); return date.toUTCString().slice(0, 11)",
weekly = "var date = new Date(x); return date.toUTCString().slice(0, 11)",
monthly = "var date = new Date(x); return date.toUTCString().slice(7, 16)",
annual = "var date = new Date(x); return date.toUTCString().slice(12, 16)",
"return x"
)
}
script <-"
function(e, timestamp, data) {
function timeToLab(x) {%s}
var tmp = document.getElementById('date%s');
if(tmp){
tmp.innerHTML = timeToLab(timestamp);
}
var values = {};
data.forEach(function(d) {
var sign = d.name.match(/^neg/) ? -1 : 1;
var varname = d.name.replace(/^neg/, '');
if (values[varname]) values[varname] += d.yval * sign;
else values[varname] = d.yval * sign;
})
for (k in values) {
if (!values.hasOwnProperty(k)) continue;
var el = document.getElementById(k + '%s');
if (el) {
if (Math.abs(values[k]) > 100 || values[k] === 0) {
el.innerHTML = Math.round(values[k]);
} else {
el.innerHTML = values[k].toPrecision(3);
}
}
}
}"
JS(sprintf(script, timeToLab, legendId, legendId))
}
JS_resetLegend <- function(legendId) {
script <- "
function(e) {
var tmp = document.getElementById('date%s');
if(tmp){
tmp.innerHTML = '';
}
var els = document.getElementsByClassName('legvalue');
if(els){
for (var i = 0; i < els.length; ++i) {els[i].innerHTML = '';}
}
}"
JS(sprintf(script, legendId))
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.