Let's say we have the following matrix:
m <- matrix(1:12, 3, 4, dimnames=list(LETTERS[1:3], LETTERS[1:4]))
and we plot that using highcharteR:
library(highcharter)
hchart(head(m, -1), "bubble", hcaes(),
showInLegend = FALSE)
we could than replace the labels of the x-axis by images, e.g. for A:
hchart(head(m, -1), "bubble", hcaes(),
showInLegend = FALSE) %>%
hc_xAxis(type = 'category',
labels = list(
formatter = JS(
"function(){
if(this.value == 'A'){
return '<img src=\"https://www.highcharts.com/samples/graphics/sun.png\"></img>';
} else {
return this.value;
}
}"
),
useHTML = TRUE
))
But how do we get the same result with base64 instead of url? Is there any way to embedd the image within the html highcharteR produces?
I tryed converting the image to base64 and embedding it into the chart:
image_path <- "https://www.highcharts.com/samples/graphics/sun.png"
image_data <- base64enc::dataURI(file=image_path, mime="image/png")
hchart(head(m, -1), "bubble", hcaes(),
showInLegend = FALSE) %>%
hc_xAxis(type = 'category',
labels = list(
formatter = JS(
sprintf(
"function(){
if(this.value == 'A'){
return '<img src=\"%s\"></img>';
} else {
return this.value;
}
}",
image_data
)
),
useHTML = TRUE
))
But that gives me no image. The output is blank and html (in the output) at this position is just: <img>
The problem is that highcharts.js is verifying the html produced by such functions as
labels.formatter
. In particular, the src
attribute has to start with one of
the values contained in Highcharts.AST.allowedReferences
, see that in
the source code.
A simple solution would be to set the src
separately, immediately after the
current javascript sequence of instruction is completed, using setTimeout
. That
means replacing
return '<img src=\"%s\"></img>';
with
setTimeout(function(){document.getElementById('img_data1').src = '%s'}, 0);
return '<img id=\"img_data1\"></img>';
A more canonical solution would be to alter Highcharts.AST.allowedReferences
by adding "data:"
as an allowed prefix. While that can be done in the
formatter
itself:
function(){
if(this.value == 'A'){
if(Highcharts.AST.allowedReferences.indexOf('data:') === -1){
Highcharts.AST.allowedReferences.push('data:')
}
return '<img src=\"%s\"></img>';
} else {
return this.value;
}
}
it would be more efficient to do the change to allowedReferences
only once,
for instance by prepending
that javascript statement to the htmlwidget produced by highcharter (or by other
initialization hooks specific to your application):
library(highcharter);
library(htmlwidgets);
library(htmltools);
m <- matrix(1:12, 3, 4, dimnames=list(LETTERS[1:3], LETTERS[1:4]))
image_path <- "https://www.highcharts.com/samples/graphics/sun.png"
image_data <- base64enc::dataURI(file=image_path, mime="image/png")
initCode <- HTML("<script>Highcharts.AST.allowedReferences.push('data:')</script>")
hchart(head(m, -1), "bubble", hcaes(),
showInLegend = FALSE) %>%
prependContent(initCode) %>%
hc_xAxis(type = 'category',
labels = list(
formatter = JS(
sprintf(
"function(){
if(this.value == 'A'){
return '<img src=\"%s\"></img>';
} else {
return this.value;
}
}",
image_data
)
),
useHTML = TRUE
))