I have a Shiny application in which a user can select black points in a Plotly scatterplot using the Plotly "box select" icon. The points the user selects will be highlighted in red. I have a MWE of this application below:
library(plotly)
library(htmlwidgets)
library(shiny)
ui <- shinyUI(fluidPage(
plotlyOutput("myPlot")
))
server <- shinyServer(function(input, output) {
p <- ggplot(mtcars, aes(x = wt, y = mpg)) + xlim(10,40) +ylim(0,10)
ggPS <- ggplotly(p)
output$myPlot <- renderPlotly(ggPS %>%
onRender("
function(el, x, data) {
var xArr = [];
var yArr = [];
for (a=0; a<data.wt.length; a++){
xArr.push(data.wt[a])
yArr.push(data.mpg[a])
}
Traces=[]
var tracePoints = {
x: yArr,
y: xArr,
hoverinfo: 'none',
mode: 'markers',
marker: {
color: 'black',
size: 4
}
};
Traces.push(tracePoints);
Plotly.addTraces(el.id, Traces);
el.on('plotly_selected', function(e) {
var numSel = e.points.length
var xSel = [];
var ySel = [];
for (a=0; a<numSel; a++){
xSel.push(e.points[a].x)
ySel.push(e.points[a].y)
}
var trace = {
x: xSel,
y: ySel,
mode: 'markers',
marker: {
color: 'red',
size: 4
},
hoverinfo: 'none'
};
Traces.push(trace);
Plotly.addTraces(el.id, Traces);
})
}
", data = list(dat= mtcars, wt=mtcars$wt, mpg=mtcars$mpg)))})
shinyApp(ui, server)
I am now trying to update this Shiny application so that the selected black points do not automatically become red. Instead, after the user selects the black points, they can click on an action button with a label "Highlight selected points". If the user clicks that action button, then the selected points become red. Below is my attempt at getting this to work. Unfortunately, this application is not working, and actually loses its functionality of drawing the original black points and providing a box select icon in the first place .
library(plotly)
library(Shiny)
library(htmlwidgets)
ui <- shinyUI(fluidPage(
plotlyOutput("myPlot"),
actionButton("highlight", "Highlight selected points")
))
server <- shinyServer(function(input, output) {
highlight <- reactive(input$highlight)
p <- ggplot(mtcars, aes(x = wt, y = mpg)) + xlim(10,40) +ylim(0,10)
ggPS <- ggplotly(p)
output$myPlot <- renderPlotly(ggPS %>%
onRender("
function(el, x, data) {
var xArr = [];
var yArr = [];
for (a=0; a<data.wt.length; a++){
xArr.push(data.wt[a])
yArr.push(data.mpg[a])
}
Traces=[]
var tracePoints = {
x: yArr,
y: xArr,
hoverinfo: 'none',
mode: 'markers',
marker: {
color: 'black',
size: 4
}
};
Traces.push(tracePoints);
Plotly.addTraces(el.id, Traces);
el.on('plotly_selected', function(e) {
observeEvent(data.highlightS, {
var numSel = e.points.length
var xSel = [];
var ySel = [];
for (a=0; a<numSel; a++){
xSel.push(e.points[a].x)
ySel.push(e.points[a].y)
}
var trace = {
x: xSel,
y: ySel,
mode: 'markers',
marker: {
color: 'red',
size: 4
},
hoverinfo: 'none'
};
Traces.push(trace);
Plotly.addTraces(el.id, Traces);
})
})
}
", data = list(dat= mtcars, wt=mtcars$wt, mpg=mtcars$mpg, highlightS=highlight())))})
shinyApp(ui, server)
EDIT:
I wanted to include a picture to demonstrate what I am aiming for. Basically, if the user selects the 15 dots shown below, they remain black:
However, if the user selects the "Highlight the selected points" Shiny button, then the 15 dots will become red as shown below:
This would have been a preferred way to do the above, but the other solution I provided works better. This is because of a plotly bug in the R-binding that scatters the key values on selection, so it only works for one iteration. Once a selection has happened, the key values will no longer work. If you try it out you will see what I mean.
Also I couldn't get the sizes to work as above, that could also be another bug, or just a weird design issue.
However I am providing it anyway because it will probably work some day, and it is a better solution requiring far less code - or maybe someone will suggest a nice workaround for it.
Ok, this does what you want I think.
I had to take a different approach since I don't think you can add a plotly
el.on
event like that, but plotly has actually a Shinyevent_data("plotly_selected")
construct intended just for this kind of thing - so I used that instead.The major changes I made:
m
) in areactiveValues
so you could access it easily (without using <<-) from inside a reactive node.reactive(event_data(..
to retreive the shiny selection data.selected
column to our working dataframe to keep track of what should be marked red.reactive
node (mvis) to process the selected data as it comes in and mark the new things as selected. Isolated the node from the reactiveValue event as well withisolate
to avoid an endless reaction chain.el.on(plotly_selected
because I don't see how that could work (although there is probably a way).layout(dragmode="select")
callxArr
andyArr
were reversed in the originaltraceBlock
definition for example)So here is the code:
And here is a screen shot:
Note:
This seems overly complex, and it is. In theory this could be done without using
onRender
and any javascript, by adding traces withadd_marker
and setting thekey
attribute. Unfortunately there seems to be a plotly bug in the R binding that scrambles the key values after selection when you do it that way. Too bad - it would be much shorter and easier to understand - maybe it will eventually get fixed.