Make only subset of shapes editable/draggable in Plotly

I am developing a shiny app to allow users to create a curve by moving points ( actually circle shapes) on a plotly scatterplot. The app updates a spline as the points are moved. I need to put some vertical lines ( and possibly rectangles on the plot "background" however these shapes should be fixed, while the circles need to be editable by dragging.

I can only get all the shapes as draggable ( by incuding "config(edits = list(shapePosition = TRUE))" in the plotly pipe.
If I do not incude this line ( but incude editable=TRUE for circles and editable = false for GSLines) then nothing is draggable how do I make it so that just Circles are draggable?

Alternatively is there another way ( other than shapes) to incude fixed vertical guidelines in the plot background?

Thanks

The code was based on add shiny example of dragging markers (for interactive lm) · plotly/plotly.R@eb12338 · GitHub

library(plotly)
library(purrr)
library(shiny)

myCircle<- function(x = 0,y=0,fillcolor = "blue",linecolor ="transparent"){
list(
    type = "circle",
    xanchor = x,
    yanchor = y,
    # give each circle a 2 pixel diameter
    x0 = -4,
    x1 = 4,
    y0 = -4,
    y1 = 4,
    xsizemode = "pixel",
    ysizemode = "pixel",
    # other visual properties
    fillcolor = fillcolor,
    line = list(color = linecolor),
    editable=TRUE
)
}

GSx<-c(2,10,20)

myline <- function(x = 0, color = "red") {
    list(
        type = "line",
        y0 = 0,
        y1 = 1,
        yref = "paper",
        x0 = x,
        x1 = x,
        line = list(color = color, dash = "dot"),
        editable=FALSE
    )
}
Lines = map2(GSx,"red",myline)




ui <- fluidPage(h3("Drag blue dots to change curve and TSF values"),
                             plotlyOutput("p")
                         )

server <- function(input, output, session) {
    rv <- reactiveValues(x = c(0, 5, 10, 50, 400),
                         y = c(1, 1, 1, 1, 1))

    observeEvent(rv$x | rv$y, {
        rv$mySpline <- as.data.frame(spline(
            rv$x,
            rv$y,
            xmin = 0,
            xmax = 400,
            n = 401
        )) %>%
            mutate(y = ifelse(y < 0, 0, y)) #restrict Y to values >=0
        
        output$p <- renderPlotly({
            # creates a list of circle shapes from x/y data
            Circles <- pmap(
                list(
                rv$x,
                rv$y,
                "blue",
                "transparent"),
                myCircle
            )
            
            
            
            # plot the shapes and fitted line
            plot_ly() %>% add_lines(
                x = ~ rv$mySpline$x,
                y = ~ rv$mySpline$y,
                line = list(color = "black")
            ) %>%
                layout(
                    shapes = c(Circles,Lines),
                    xaxis = list(title = "Years Since Fire",
                                 range = c(0, 400)),
                    yaxis = list(title = "Estimated abundance",
                                 range = c(0, 1.5 * (
                                     max(rv$mySpline$y)
                                 )))
                ) %>%
                config(edits = list(shapePosition = TRUE))
        })
        
       
       
    })
    # update x/y reactive values in response to changes in shape anchors
    observe({
        ed <- event_data("plotly_relayout")
        shape_anchors <<- ed[grepl("^shapes.*anchor$", names(ed))]
        if (length(shape_anchors) != 2) {
            return()
        }
        row_index <-
            unique(readr::parse_number(names(shape_anchors)) + 1)
        pts <- as.numeric(shape_anchors)
        rv$x[row_index] <-
            ifelse(pts[1] > 400, 400, ifelse(pts[1] < 0, 0, pts[1]))
        rv$y[row_index] <- ifelse(pts[2] < 0, 0, pts[2])
        xx <<- rv$x
        yy <<- rv$y
    })
    
}

shinyApp(ui, server)

according to this, there are different behaviours.
If you use config, then its as you were, instant dragging, but any shape can be dragged.
If you dont use config, you can set certain shapes to editable as you've attempted, however the shape has to be first clicked before it can be dragged, and well, the event_data returned looks different in this mode than the other. It doesn't have the anchor label that was relied upon. I also couldnt immediately tell what numerical scale the x0, and y1 values returned in the event data related to in this newer mode.

Perhaps sticking with the old approach is preferable. I think it may be easier to let the guide lines be draggable but have your code 'snap them' back to their place if that happens Thats if they are shapes.

Then again, perhaps you can try plotly add_segments or add_lines to rig up your static verticals as non-shapes, as was an idea you mentioned in your post , I think thats a good approach

Thanks for the suggestions and finding that explanation of the differences for me!

As you say the new editable method event data does something wierd to the scales - when you drag one of the circles it does not plot at the point you dragged it to in my caseit was being offest outside the x axis of the current chart - so was easily lost - and the positioning was not intutive. Futher the UI requirement to select by clicking , then unselect by clicking again is ( for my app at least) less intuitve than a simple drag and drop ( which the old config.edists.shapePostion allows.

The advantage of using shapes if I could, rather than add_lines is that the y coordinates can be made relative to the "paper" ( so the line spans the chart at the nominated x value evern where the y axis changes as points are dragged. The work around of setting the Y value for added lines to be very large causes problems with the autoscale button - resulting in the chart scaling to this very large ( and irrelevant y value rather than the max y value for the circles. One work around creating another problem .

This topic was automatically closed 21 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.