Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Open hyperlink on click on an ggplot/plotly chart

This is a follow up question on the answer provided for Add onclick open hyperlink event to an html widget created in R. Consider the following example:

library(ggplot2)
library(plotly)
library(htmlwidgets)
library(htmltools)
myData <- data.frame(
  x=c(1,2,3), 
  y=c(3,2,1),
  label=c("Google", "Bing", "R"),
  category=c("search", "search", "other"),
  urls=c("http://google.de", "http://bing.com", "http://r-project.org")
)

f <- function(p) {
  ply <- ggplotly(p)
  javascript <- HTML(paste("
   var myPlot = document.getElementById('", ply$elementId, "');
   myPlot.on('plotly_click', function(data){
   var urls = ['", paste(myData$urls, collapse = "', '"), "'];
   window.open(urls[data.points[0].pointNumber],'_blank');
   });", sep=''))  
  prependContent(ply, onStaticRenderComplete(javascript))
}

This works as expected - a click on any point opens the corresponding url:

f(ggplot(myData, aes(x=x, y=y)) + geom_point(aes(text=label)))

enter image description here

This does not work as expected - the indices do not match anymore:

f(ggplot(myData, aes(x=x, y=y)) + geom_point(aes(text=label, color=category)))

enter image description here

The plotly objects differ and it seems as if pointNumber does not hold the absolute index of all points any more, but the index within a (color) grouping.

Any ideas on how to adapt the example so that it works for general use cases also with color/fill groupings?

like image 478
lukeA Avatar asked Apr 07 '18 19:04

lukeA


1 Answers

  • The plotly_click event provides the name of the data trace.
  • data is the click event information
  • data.points[0].data.name is the trace/category name

Instead of passing the flattened data frame we can split it by category (just like aes does) and pass into our JavaScript function

var urls = ", toJSON(split(myData, myData$category)), ";
                       

Which gives us the following JSON

{"other": [{"x":3,"y":1,"label":"R","category":"other","urls":"http://r-project.org"}],
 "search":[{"x":1,"y":3,"label":"Google","category":"search","urls":"http://google.de"},
           {"x":2,"y":2,"label":"Bing","category":"search","urls":"http://bing.com"}]
} 

The URL is then retrieved by

window.open(urls[data.points[0].data.name][data.points[0].pointNumber]['urls'],'_blank');
                       

i.e. from the provided JSON: we take from the first (and only) point which was clicked on (data.points[0]) the name of the trace (data.name) and its pointNumber (i.e. the n-th point in the trace).


Complete code

library(ggplot2)
library(plotly)
library(htmlwidgets)
library(htmltools)
library(jsonlite)

myData <- data.frame(
  x=c(1,2,3), 
  y=c(3,2,1),
  label=c("Google", "Bing", "R"),
  category=c("search", "search", "other"),
  urls=c("http://google.de", "http://bing.com", "http://r-project.org")
)

f <- function(p) {
  ply <- ggplotly(p)
  
  javascript <- HTML(paste("
                           var myPlot = document.getElementsByClassName('js-plotly-plot')[0];
                           myPlot.on('plotly_click', function(data){
                           var urls = ", toJSON(split(myData, myData$category)), ";
                           window.open(urls[data.points[0].data.name][data.points[0].pointNumber]['urls'],'_blank');
                           });", sep=''))  
  prependContent(ply, onStaticRenderComplete(javascript))
}

f(ggplot(myData, aes(x=x, y=y)) + geom_point(aes(text=label, color=category)))
like image 125
Maximilian Peters Avatar answered Oct 20 '22 19:10

Maximilian Peters