Add shaded rectangle with select Box corners in Pl

2019-07-27 05:56发布

I am trying to create an application where if a user employs the Box Select tool in Plotly, then a filled rectangle will appear alongside the Box Select. To accomplish this, I am trying to call Plotly.addTrace() to create a rectangle object (type: rect) that has the corners of the box the user selected.

I am able to obtain the coordinates of the corners of the box the user selected (in the code they are called xMin, xMax, yMin, and yMax). However, I then try to create a variable called drawRect that can be added to Plotly. This does nothing at this time, though.

I based some of the syntax off a post about Python Plotly (https://plot.ly/python/shapes/). However, I am working with Plotly in R. Is there any similar rectangle object that I could use in Plotly R?

Below is MWE showing my code.

library(ggplot2)
library(shiny)
library(plotly)
library(htmlwidgets)

ui <- basicPage(
  plotlyOutput("plot1")
)

server <- function(input, output) {

  p <- ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point(alpha=0) + xlim(0,5) +ylim(-3,3)
  gp <- ggplotly(p)

  set.seed(3)
  myDF <- data.frame(X1=rnorm(10,-1), X2=rnorm(10,-1), X3=rnorm(10,-1), X4=rnorm(10,1), X5=rnorm(10,1), X6=rnorm(10,1))
  colNms <- colnames(myDF)
  nVar <- length(colNms)

  output$plot1 <- renderPlotly({
    gp %>% onRender("
    function(el, x, data) {

    var myDF = data.myDF
    var Traces = [];
    var dLength = myDF.length
    var vLength = data.nVar
    var cNames = data.colNms
    for (a=0; a<dLength; a++){
      xArr = [];
      yArr = [];
      for (b=0; b<vLength; b++){
        xArr.push(b)
        yArr.push(myDF[a][cNames[b]]);
      }
      var pcpLine = {
        x: xArr,
        y: yArr,
        mode: 'lines',
        line: {
          color: 'orange',
          width: 1
        },
        opacity: 0.9,
      }
      Traces.push(pcpLine);
    }
    Plotly.addTraces(el.id, Traces);

    el.on('plotly_selected', function(e) {
      var dLength = myDF.length
      var selectedPCP = []
      var xMin = e.range.x[0]
      var xMax = e.range.x[1]
      var yMin = e.range.y[0]
      var yMax = e.range.y[1]

      console.log([xMin, xMax, yMin, yMax])

      var Traces = []
      var drawRect = {
        type: 'rect',
        x0: xMin,
        y0: yMin,
        x1: xMax,
        y1: yMax,
        line: {
          color: 'green',
          width: 1
        },
        fillcolor: 'green'
      }
      Traces.push(drawRect);
      Plotly.addTraces(el.id, Traces);
    })
    }", data = list(myDF = myDF, nVar = nVar, colNms = colNms))})

    }
    shinyApp(ui, server)

1条回答
别忘想泡老子
2楼-- · 2019-07-27 06:35

Basically, strange as it may sound to us old ggplot vets, "shapes" are not handled the same way as markers and lines (which are set in "traces"), but "shapes" (like rect) are set in the layout part of a plotly plot. Probably I would understand this better if I read more of the plotly book..

So I made two changes.

  • Changed the dragmode to "select" using layout at the begining.
  • Changed the Plotly.addTraces logic at the end of your onRender javascript to Plotly.relayout

As a sidenote I have been wondering for hours why it was called "relay-out", I just now realized as I was writing this that it is "re-layout" which makes complete sense.

So here is the code:

library(ggplot2)
library(shiny)
library(plotly)
library(htmlwidgets)

ui <- basicPage(
  plotlyOutput("plot1")
)

server <- function(input, output) {

  p <- ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point(alpha=0) + xlim(0,5) +ylim(-3,3)
  gp <- ggplotly(p)

  set.seed(3)
  myDF <- data.frame(X1=rnorm(10,-1), X2=rnorm(10,-1), X3=rnorm(10,-1), X4=rnorm(10,1), X5=rnorm(10,1), X6=rnorm(10,1))
  colNms <- colnames(myDF)
  nVar <- length(colNms)

  output$plot1 <- renderPlotly({
    gp %>% layout(dragmode="select") %>%  
           onRender("
                    function(el, x, data) {

                    var myDF = data.myDF
                    var Traces = [];
                    var dLength = myDF.length
                    var vLength = data.nVar
                    var cNames = data.colNms
                    for (a=0; a<dLength; a++){
                    xArr = [];
                    yArr = [];
                    for (b=0; b<vLength; b++){
                    xArr.push(b)
                    yArr.push(myDF[a][cNames[b]]);
                    }
                    var pcpLine = {
                    x: xArr,
                    y: yArr,
                    mode: 'lines',
                    line: {
                    color: 'orange',
                    width: 1
                    },
                    opacity: 0.9,
                    }
                    Traces.push(pcpLine);
                    }
                    Plotly.addTraces(el.id, Traces);

                    el.on('plotly_selected', function(e) {
                    var dLength = myDF.length
                    var selectedPCP = []
                    var xMin = e.range.x[0]
                    var xMax = e.range.x[1]
                    var yMin = e.range.y[0]
                    var yMax = e.range.y[1]

                    console.log([xMin, xMax, yMin, yMax])

                    var Traces = []
                    var drawRect = {
                    type: 'rect',
                    x0: xMin,
                    y0: yMin,
                    x1: xMax,
                    y1: yMax,
                    line: {
                    color: 'green',
                    width: 1
                    },
                    fillcolor: 'green'
                    }
                    var update = {
                        shapes:[drawRect]
                     }
                    Plotly.relayout(el.id, update)
                    })
                    }", data = list(myDF = myDF, nVar = nVar, colNms = colNms))})

  }
shinyApp(ui, server)

And here is a screen shot of it working:

enter image description here

查看更多
登录 后发表回答