Hi Joe,
Thanks so much for the reply! Your solutions are really smart.
So the first version passed the components needed to reference the value in variables, then the dynamically assembled expression access the value we want directly.
Then the second version focused on the reactive value of "range" and return it only, with the observeEvent happened as side effect. In this way we don't even need a global reactive value to hold the range, since it's just some operations applied to brush values.
Though that lead me to think maybe we don't need the extra layer of rv$range, and we also don't need the reactive wrapper. I put all versions in same app to test it, the plot 5 with my modifications seemed work, but I'm not sure if there is any problem with it.
full code below, also in attachment if that is easier to copy
-----------------------------
library(ggplot2)
library(shiny)
options(shiny.trace = TRUE)
ui <- fluidPage(
fluidRow(
column(width = 4, class = "well",
h4("zoom1"),
plotOutput("plot1", height = 300,
dblclick = "plot1_dblclick",
brush = brushOpts(
id = "plot1_brush",
resetOnNew = TRUE
)
)
),
column(width = 4, class = "well",
h4("zoom2"),
plotOutput("plot2", height = 300,
dblclick = "plot2_dblclick",
brush = brushOpts(
id = "plot2_brush",
resetOnNew = TRUE
)
)
),
column(width = 4, class = "well",
h4("zoom3"),
plotOutput("plot3", height = 300,
dblclick = "plot3_dblclick",
brush = brushOpts(
id = "plot3_brush",
resetOnNew = TRUE
)
)
)
),
fluidRow(
column(width = 4, class = "well",
h4("zoom4"),
plotOutput("plot4", height = 300,
dblclick = "plot4_dblclick",
brush = brushOpts(
id = "plot4_brush",
resetOnNew = TRUE
)
)
),
column(width = 4, class = "well",
h4("zoom5"),
plotOutput("plot5", height = 300,
dblclick = "plot5_dblclick",
brush = brushOpts(
id = "plot5_brush",
resetOnNew = TRUE
)
)
)
)
)
server <- function(input, output) {
# plot 1, add zoom with regular approach ----
values <- reactiveValues()
values$plot1_ranges <- reactiveValues(x = NULL, y = NULL)
output$plot1 <- renderPlot({
ggplot(mtcars, aes(wt, mpg)) +
geom_point() +
coord_cartesian(xlim = values$plot1_ranges$x, ylim = values$plot1_ranges$y)
})
# When a double-click happens, check if there's a brush on the plot.
# If so, zoom to the brush bounds; if not, reset the zoom.
observeEvent(input$plot1_dblclick, {
brush <- input$plot1_brush
if (!is.null(brush)) {
values$plot1_ranges$x <- c(brush$xmin, brush$xmax)
values$plot1_ranges$y <- c(brush$ymin, brush$ymax)
} else {
values$plot1_ranges$x <- NULL
values$plot1_ranges$y <- NULL
}
})
# plot 2, call function in observeEvent expression parameter ----
values$plot2_ranges <- reactiveValues(x = NULL, y = NULL)
add_zoom <- function(plot_id, reactive_ranges) {
brush <- input[[paste0(plot_id, "_brush")]]
if (!is.null(brush)) {
reactive_ranges$x <- c(brush$xmin, brush$xmax)
reactive_ranges$y <- c(brush$ymin, brush$ymax)
} else {
reactive_ranges$x <- NULL
reactive_ranges$y <- NULL
}
}
output$plot2 <- renderPlot({
ggplot(mtcars, aes(wt, mpg)) +
geom_point() +
coord_cartesian(xlim = values$plot2_ranges$x, ylim = values$plot2_ranges$y)
})
observeEvent(input$plot2_dblclick, add_zoom("plot2", values$plot2_ranges))
# plot 3, use varialbe$slot separately ----
add_zoom_slot <- function(plot_id, rvalues, slot) {
rvalues[[slot]] <- list(x = NULL, y = NULL)
observeEvent(input[[paste0(plot_id, "_dblclick")]], {
brush <- input[[paste0(plot_id, "_brush")]]
if (!is.null(brush)) {
rvalues[[slot]]$x <- c(brush$xmin, brush$xmax)
rvalues[[slot]]$y <- c(brush$ymin, brush$ymax)
} else {
rvalues[[slot]]$x <- NULL
rvalues[[slot]]$y <- NULL
}
})
}
output$plot3 <- renderPlot({
ggplot(mtcars, aes(wt, mpg)) +
geom_point() +
coord_cartesian(xlim = values$plot3_ranges$x, ylim = values$plot3_ranges$y)
})
add_zoom_slot("plot3", values, "plot3_ranges")
# plot 4, reactive expression ----
add_zoom_exp <- function(plot_id) {
rv <- reactiveValues(range = list(x = NULL, y = NULL))
observeEvent(input[[paste0(plot_id, "_dblclick")]], {
brush <- input[[paste0(plot_id, "_brush")]]
if (!is.null(brush)) {
rv$range$x <- c(brush$xmin, brush$xmax)
rv$range$y <- c(brush$ymin, brush$ymax)
} else {
rv$range$x <- NULL
rv$range$y <- NULL
}
})
reactive(rv$range)
}
plot4_range <- add_zoom_exp("plot4")
output$plot4 <- renderPlot({
ggplot(mtcars, aes(wt, mpg)) +
geom_point() +
coord_cartesian(xlim = plot4_range()$x, ylim = plot4_range()$y)
})
# plot 5, reactive expression with less level ----
add_zoom_exp_2 <- function(plot_id) {
ranges <- reactiveValues(x = NULL, y = NULL)
observeEvent(input[[paste0(plot_id, "_dblclick")]], {
brush <- input[[paste0(plot_id, "_brush")]]
if (!is.null(brush)) {
ranges$x <- c(brush$xmin, brush$xmax)
ranges$y <- c(brush$ymin, brush$ymax)
} else {
ranges$x <- NULL
ranges$y <- NULL
}
})
ranges
}
plot5_range <- add_zoom_exp_2("plot5")
output$plot5 <- renderPlot({
ggplot(mtcars, aes(wt, mpg)) +
geom_point() +
coord_cartesian(xlim = plot5_range$x, ylim = plot5_range$y)
})
}
shinyApp(ui = ui, server = server)