Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proxy functions should be deferred until the shiny session is flushed by default #17

Open
RWParsons opened this issue Jul 16, 2024 · 2 comments

Comments

@RWParsons
Copy link
Contributor

RWParsons commented Jul 16, 2024

This bug was identified in the discussions of #12.

Default values for shiny inputs that are observed to modify the map via proxy function are not currently applied. This is because they are not deferred until the shiny session is flushed as is the default by leaflet. See the that the proxy function includes a deferUntilFlush argument (https://github.com/rstudio/leaflet/blob/92bc272caa9a268140e75ede1966bcdc7d585636/R/utils.R#L136) and the invocation of this check this value and defers the change until the shiny session is flushed (https://github.com/rstudio/leaflet/blob/92bc272caa9a268140e75ede1966bcdc7d585636/R/utils.R#L194).

reprex:

By default this app should show only one polygon on the map (with the current default inputs for the 'area_range'. If you reduce the lower bound of the input range, it updates the map but this should happen for the default view of the map.

library(shiny)
library(bslib)
library(colourpicker)
library(dplyr)
library(sf)
library(shinyWidgets)
devtools::load_all()

ui <- bootstrapPage(
  sliderInput("slider", "min value:", value = 0, min = -3, max = 3),
  numericRangeInput("area_range", label = "numeric range input for area", value = c(0.03, 0.042), step = 0.01),
  maplibreOutput("map")
)



server <- function(input, output, session) {
  nc <- st_read(system.file("shape/nc.shp", package = "sf"))
  nc$var1 <- rnorm(n = nrow(nc))
  nc$CNTY_ID <- as.character(nc$CNTY_ID)

  output$map <- renderMaplibre({
    maplibre() |>
      fit_bounds(nc, animate = FALSE) |>
      add_fill_layer(
        id = "polygon_layer",
        source = nc,
        fill_color = "blue",
        fill_opacity = 0.5,
        tooltip = "AREA"
      )
  })


  observe({
    ids <- nc |>
      filter(
        AREA >= min(input$area_range),
        AREA <= max(input$area_range)
      ) |>
      pull(CNTY_ID)

    cat(length(ids))

    maplibre_proxy("map") |>
      set_filter(
        "polygon_layer",
        list("in", "CNTY_ID", ids)
      )
  })
}

shinyApp(ui, server)

I think the solution would be to make some wrapper, like leaflet has invokeRemote() which checks the shiny session and defers if necessary, rather than each shiny util, like set_filter(), directly calling proxy$session$sendCustomMessage().

Thanks!

Rex

@walkerke
Copy link
Owner

I've pushed a temporary fix here that allows set_filter() to be used on a regular map object (within or outside of Shiny). So an app developer can hard-code the initial filter to correspond to the initial value of the slider and get the expected behavior. I'll keep looking into this solution as well though.

@walkerke
Copy link
Owner

Example usage:

library(shiny)
library(bslib)
library(colourpicker)
library(dplyr)
library(sf)
library(shinyWidgets)
devtools::load_all()

ui <- bootstrapPage(
  sliderInput("slider", "min value:", value = 0, min = -3, max = 3),
  numericRangeInput("area_range", label = "numeric range input for area", value = c(0.03, 0.042), step = 0.01),
  maplibreOutput("map")
)



server <- function(input, output, session) {
  nc <- st_read(system.file("shape/nc.shp", package = "sf"))
  nc$var1 <- rnorm(n = nrow(nc))
  nc$CNTY_ID <- as.character(nc$CNTY_ID)
  
  output$map <- renderMaplibre({
    
    initial_ids <- nc |>
      filter(
        AREA >= 0.03,
        AREA <= 0.042
      ) |>
      pull(CNTY_ID)
    
    maplibre() |>
      fit_bounds(nc, animate = FALSE) |>
      add_fill_layer(
        id = "polygon_layer",
        source = nc,
        fill_color = "blue",
        fill_opacity = 0.5,
        tooltip = "AREA"
      ) |>
      set_filter(
        "polygon_layer",
        c("in", "CNTY_ID", initial_ids)
      )
  })
  
  
  observe({
    ids <- nc |>
      filter(
        AREA >= min(input$area_range),
        AREA <= max(input$area_range)
      ) |>
      pull(CNTY_ID)
    
    cat(length(ids))
    
    maplibre_proxy("map") |>
      set_filter(
        "polygon_layer",
        c("in", "CNTY_ID", ids)
      )
  })
}

shinyApp(ui, server)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants