Black Lives Matter. Please consider donating to Black Girls Code today.
Dash Enterprise delivers an incredible 21x cost savings 💸Download the e-book!

Adding points with Plotly.addTraces() but not allowing them to be selectable by user

I am using ggplot2(), plotly, and onRender() from htmlWidgets.

Basically, I have a scatterplot with 20 black points. When the user selects (Using, say “Box select” from plotly) a subset of these points, they become red. For the most part, this seems to be working correctly.

However, I notice that if the user selects over points that have already become red (i.e. selects a point that they have already selected before), then other black points in the scatterplot that were not selected accidentally become red.

I am not sure, but I suspect the issue is from the fact that when a user selects a point, I simply overlay a new point in red on top of it (so it appears the point become red). So, when the user reselects a red point, they are actually selecting >1 points (the red point on top and the black original point beneath it).

I am hoping one way to solve this is to force the red points (made in the Plotly.addTraces() function) to not be “selectable” by users. That way, when a user selects a red point, they will only be selecting the original black point beneath it, and in theory, a new red point will be overlaid (appearing like nothing changed to the user).

Is it possible to achieve this functionality? My MWE is below:

library(plotly)
library(GGally)
library(htmlwidgets)

set.seed(1)
dat <- data.frame(ID = paste0(“ID”,sample(c(1:20),20)), A=rnorm(20), B=rnorm(20))
dat$ID <- as.character(dat$ID)

minVal = min(dat[,-1])
maxVal = max(dat[,-1])

p <- ggplot(data = dat, aes(x=A, y=B)) + coord_cartesian(xlim = c(minVal, maxVal), ylim = c(minVal, maxVal))

ggPS <- ggplotly§

myLength <- length(ggPS[[“x”]][[“data”]])
for (i in 1:myLength){
item =ggPS[[“x”]][[“data”]][[i]]$text[1]
if (!is.null(item))
if (!startsWith(item, “co”)){
ggPS[[“x”]][[“data”]][[i]]$hoverinfo <- “none”
}
}

ggPS %>% onRender("
function(el, x, data) {

   var Points = [];
   var Traces = [];
   var selRows = [];
   data.dat.forEach(function(row){selRows.push(row);})
   var xArr = [];
   var yArr = [];
   var keepIndex = [];
   for (a=0; a<selRows.length; a++){
     xArr.push(selRows[a]['A'])
     yArr.push(selRows[a]['B'])
     keepIndex.push(selRows[a]['ID'])
   }

   Points.push(keepIndex);

   var tracePoints = {
     x: xArr,
     y: yArr,
     hoverinfo: 'none',
     mode: 'markers',
     marker: {
       color: 'black',
       size: 4
     }
   };
   Traces.push(tracePoints);
   Plotly.addTraces(el.id, Traces);

   var idRows = []
   for (a=0; a<data.dat.length; a++){
   idRows.push(data.dat[a]['ID'])
   }

   el.on('plotly_selected', function(e) {

   numSel = e.points.length
   cN = e.points[0].curveNumber;

   var pointNumbers = [];
   var selData = [];
   for (a=0; a<numSel; a++){
   pointNumbers.push(e.points[a].pointNumber)
   selData.push(data.dat[idRows.indexOf(Points[0][pointNumbers[a]])])
   }

   var Traces = [];
   var xArr = [];
   var yArr = [];
   for (a=0; a<selData.length; a++){
     xArr.push(selData[a]['A'])
     yArr.push(selData[a]['B'])
   }
   var trace = {
     x: xArr,
     y: yArr,
     mode: 'markers',
     marker: {
       color: 'red',
       size: 4
     },
     hoverinfo: 'none'
   };
   Traces.push(trace);

   Plotly.addTraces(el.id, Traces);
   })

   }
   ", data = list(dat=dat))