Skip to contents

This function sets up server-side infrastructure to support draggable and resizable overlays on a plot. This may be useful in applications where users need to define regions on the plot for further input or processing. Currently, the overlays are only designed to move along the x axis of the plot.

Usage

overlayServer(
  outputId,
  nrect,
  width = NULL,
  data = NULL,
  snap = NULL,
  heading = NULL,
  select = NULL,
  menu = NULL,
  colours = overlayColours,
  opacity = 0.25,
  icon = shiny::icon("gear"),
  stagger = 0.045,
  style = list(),
  debug = FALSE
)

Arguments

outputId

The ID of the plot output (as used in overlayPlotOutput()).

nrect

Number of overlay rectangles to support.

width

Optional default overlay width in plot coordinates. If NULL (default), set to 10% of the plot width.

data

Named list of custom overlay-specific properties to be edited in the overlay dropdown menu.

snap

Function to "snap" overlay coordinates to a grid, or NULL (default) for no snapping. See details for how to specify the snap function; you can also use the built-in snapGrid().

heading

Function to provide a heading for the overlay dropdown menus, or NULL (default) for no heading. See details for how to specify the heading function; you can also use the built-in rangeHeading() or dateHeading().

select

If you want to allow users to change the type (i.e. label) of the overlay from the overlay dropdown menu, set this to TRUE to provide a select input with all labels or a character vector with permissible choices. NULL (default) to omit this feature.

menu

Function to provide additional UI elements on the overlay dropdown menu. See details for how to specify the menu function.

colours

A function to assign custom colours to the overlays. Should be a function that takes a single integer (the number of overlays) and returns colours in hexadecimal notation (e.g. "#FF0000"). Do not provide opacity here as a fourth channel; use the opacity argument instead.

opacity

Numeric value (0 to 1) indicating overlay transparency.

icon

A Shiny icon to show the dropdown menu.

stagger

Vertical offset between stacked overlays, as a proportion of height.

style

Named list of character vectors with additional CSS styling attributes for the overlays. If an element is named "background-color" then this will override the colours and opacity arguments. Vectors are recycled to length nrect.

debug

If TRUE, prints changes to input values to the console for debugging purposes.

Value

A shiny::reactiveValues() object with the following named fields:

nNumber of overlays (read-only).
showTRUE/FALSE; controls whether overlays are visible.
activeLogical vector of length n; indicates which overlays are active.
labelCharacter vector of labels shown at the top of each overlay.
dataCustom data for each overlay, to be edited via the dropdown menu.
editingIndex of the overlay currently being edited via the dropdown menu; NA if none (read-only).
lastIndex of the most recently added overlay (read-only).
snapCoordinate snapping function.
headingHeading function for the dropdown menu.
selectOverlay label select options for the dropdown menu.
menuFunction to provide additional UI elements for the dropdown menu.
px,pwNumeric vector; overlay x-position and width in pixels (see note).
py,phNumeric vector; overlay y-position and height in pixels (read-only).
cx0,cx1Numeric vector; overlay x-bounds in plot coordinates (see note).
outputIdThe output ID of the plot display area (read-only).
bound_cx, bound_cwx-position and width of the bounding area in plot coordinates (read-only).
bound_px, bound_pwx-position and width of the bounding area in pixels (read-only).
bound_py, bound_phy-position and height of the bounding area in pixels (read-only).
staggerAmount of vertical staggering, as proportion of height.
styleNamed list of character vectors; additional styling for rectangular overlays.
update_cx(i)Function to update cx0/cx1 from px/pw for overlays i (see note).
update_px(i)Function to update px/pw from cx0/cx1 for overlays i (see note).

Note: Fields marked "read-only" above should not be changed. Other fields can be changed in your reactive code and this will modify the overlays and their properties. The fields px and pw which specify the pixel coordinates of each overlay can be modified, but any modifications should be placed in a shiny::isolate() call, with a call to ov$update_cx(i) at the end to update cx0 and cx1 and apply snapping. Similarly, the fields cx0 and cx1 which specify the plot coordinates of each overlay can be modified, but modifications should be placed in a shiny::isolate() call with a call to ov$update_px(i) at the end to update px and pw and apply snapping. The i parameter to these functions can be left out to apply changes to all overlays, or you can pass in the indices of just the overlay(s) to be updated.

Details

Call this function once from your server code to initialise a set of overlay rectangles for a specific plot. It creates reactive handlers for move, resize, and dropdown menu actions, and allows adding new overlays by dragging an overlayToken() onto the plot. The function returns a shiny::reactiveValues() object which you should keep for further use; in the examples and documentation, this object is typically called ov.

snap parameter

If you provide your own coordinate snapping function (snap argument), it should have the signature function(ov, i) where ov is the shiny::reactiveValues() object defining the overlays and their settings, and i is the set of indices for the rectangles to be updated. When the position of any of the overlays is changed, the snapping function will be applied. In this function, you should make sure that all ov$cx0[i] and ov$cx1[i] are within the coordinate bounds defined by the plot, i.e. constrained by ov$bound_cx and ov$bound_cw, when the function returns. This means, for example, if you are "rounding down" ov$cx0[i] to some nearest multiple of a number, you should make sure it doesn't become less than ov$bound_cx. Finally, the snapping function will get triggered when the x axis range of the plot changes, so it may be a good idea to provide one if the user might place an overlay onto the plot, but then change the x axis range of the plot such that the overlay is no longer visible. You can detect this by verifying whether the overlay rectangles are "out of bounds" at the top of your snapping function. See the code for snapGrid() for ideas.

Overlay dropdown menu

Overlays have a little icon in the top-right corner (by default, a gear). When the user clicks on this icon, a dropdown menu appears that allows the user to remove the overlay. You can also provide additional components for this dropdown menu by using the heading, select, and menu parameters to overlayServer().

heading: This should be a function with the signature function(ov, i) where ov is the shiny::reactiveValues() object defining the overlays and their settings, and i is the (single) index for the current overlay. The function should return a character string that will be used as the heading on thedropdown menu. This can be used to e.g. report the precise start and end point of the overlay, which may be useful to your users. The built-in functions rangeHeading() and dateHeading() can be used for numeric values and date values on the x-axis, respectively. Or you can use NULL for no heading on the dropdown menu.

select: This can be TRUE to provide a shiny::selectInput() widget on the dropdown menu that users can use to change the type (i.e. label) of the current overlay. Or you can provide a character vector to restrict the widget to specific labels, or use NULL to omit this widget.

menu: This can be a function with the signature function(ov, i) where ov is the shiny::reactiveValues() object defining the overlays and their settings, and i is the (single) index for the current overlay. It should return UI component(s) (if multiple components, wrapped in a list or tagList) that will be inserted into the dropdown menu. If you give the input widgets special IDs, the user can use those input widgets to directly modify certain properties of the overlays:

inputIdModifies
*_labelThe label of the overlay currently being edited.
*_cx0Starting x-coordinate of overlay.
*_cx1Ending x-coordinate of overlay.
*_cxX-position of overlay; this is like cx0, but also updates cx1 to keep the same width.
*_cwWidth of overlay; this adjusts cx1 so that the overlay has the given width.
*_XYZThe corresponding entry "XYZ" in data for the overlay being edited.
Note: above, * stands for the outputId argument to overlayServer().

See examples for an illustration of this.

Examples

ui <- shiny::fluidPage(
    overlayPlotOutput("my_plot", 640, 480),
    overlayToken("add", "Raise")
    # further UI elements here . . .
)

server <- function(input, output) {
    menu <- function(ov, i) {
        sliderInput("my_plot_amount", "Raise amount",
               value = ov$data$amount[i], min = 0, max = 1)
    }

    ov <- overlayServer("my_plot", 4, 1,
        data = list(amount = 0.2),
        snap = snapGrid(step = 0.1),
        heading = rangeHeading(digits = 3),
        menu = menu)

    output$my_plot <- shiny::renderPlot({
        df <- data.frame(x = seq(0, 2 * pi, length.out = 200))
        df$y <- (1 + sin(df$x)) / 2
        for (i in which(ov$active)) {
            xi <- (df$x >= ov$cx0[i] & df$x <= ov$cx1[i])
            df$y[xi] <- df$y[xi] + ov$data$amount[i]
        }
        plot(df, type = "l")
        overlayBounds(ov, "base")
    })
    # further server code here . . .
}

if (interactive()) {
    shiny::shinyApp(ui, server)
}