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:


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.onevent 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 areactiveValuesso you could access it easily (without using <<-) from inside a reactive node.reactive(event_data(..to retreive the shiny selection data.selectedcolumn to our working dataframe to keep track of what should be marked red.reactivenode (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 withisolateto avoid an endless reaction chain.el.on(plotly_selectedbecause I don't see how that could work (although there is probably a way).layout(dragmode="select")callxArrandyArrwere reversed in the originaltraceBlockdefinition 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
onRenderand any javascript, by adding traces withadd_markerand setting thekeyattribute. 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.