mapview icon indicating copy to clipboard operation
mapview copied to clipboard

Problem with dynamic map in Shiny app

Open kent37 opened this issue 4 years ago • 8 comments

I get the error

Error in .getReactiveEnvironment()$currentContext() : 
  Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)

when using mapview in a Shiny app where the map is based on reactive data. Here is an example:

library(shiny)
library(sf)
library(mapview)

ui <- fluidPage(
  sidebarLayout(sidebarPanel(
    selectInput( "county", "County Name",
      choices = c("All", levels(franconia$NAME_ASCI)),
      selected = "All"
    )
  ),
  
  mainPanel(mapviewOutput("mapPlot")))
)

server <- function(input, output) {
  fran <- reactive({
    f <- franconia
    if (input$county != "All")
      f <- franconia %>% filter(NAME_ASCI == input$county)
    f
  })
  
  mapPlot <- reactive({ mapview(fran(), zcol = "NAME_ASCI") })
  
  output$mapPlot <- renderMapview(mapPlot())
}

# Run the application 
shinyApp(ui = ui, server = server)

My guess is that the problem is the mapview2leaflet(expr) call in renderMapview, because the same example works if converted to use leaflet instead of mapview.

One fix is to enclose the output$mapPlot in observe({ }). However according to this discussion that is not recommended.

kent37 avatar Jan 07 '20 23:01 kent37

Here is the (working) leaflet version:

library(shiny)
library(sf)
library(leaflet)

ui <- fluidPage(
  sidebarLayout(sidebarPanel(
    selectInput("county", "County Name",
      choices = c("All", levels(franconia$NAME_ASCI)),
      selected = "All"
    )
  ),
  
  mainPanel(leafletOutput("mapPlot")))
)

server <- function(input, output) {
  fran <- reactive({
    f <- franconia
    if (input$county != "All")
      f <- franconia %>% filter(NAME_ASCI == input$county)
    f
  })
  
  mapPlot <- reactive({ addPolygons(leaflet(fran())) })

  output$mapPlot <- renderLeaflet(mapPlot())
}

# Run the application 
shinyApp(ui = ui, server = server)

kent37 avatar Jan 07 '20 23:01 kent37

From here: https://stackoverflow.com/questions/59585109/filtering-data-reactively-to-generate-maps/59585256

kent37 avatar Jan 07 '20 23:01 kent37

Replacing renderMapview with the below function makes this example work. I don't really understand what substitute is doing here so I don't know if there might be other implications to this change...

myRenderMapview = function (expr, env = parent.frame(), quoted = FALSE) 
{
    if (!quoted) 
        expr = substitute(mapview:::mapview2leaflet(expr))
    htmlwidgets::shinyRenderWidget(expr, leafletOutput, env, 
        quoted = TRUE)
}

kent37 avatar Jan 08 '20 13:01 kent37

See my reply on stackoverflow: In shiny applications, use of renderLeaflet over the @map slot of the mapview object is preferable and should solve your issue (See https://github.com/r-spatial/mapview/issues/58).

lbusett avatar Jan 09 '20 16:01 lbusett

ISTM that renderMapview should either be fixed or removed (or deprecated with a note to use renderLeaflet(map@map) instead.

kent37 avatar Jan 09 '20 16:01 kent37

Trying to use myRenderMapview() results in Error: addResourcePath called with invalid prefix; please see documentation.

The renderLeaflet() @map workaround works for me.

pat-s avatar Sep 17 '20 08:09 pat-s

@kent37 thanks! the myRenderMapview() worked for me!

rvalavi avatar Feb 04 '22 00:02 rvalavi

Thank you @kent37, the problem with rendering in shiny was exactly what you mentioned there. I was printing the map successfully but it was not rendered by shiny because it should be map@map not just map.

I had something like:
leaflet::renderLeaflet({mapview(SomeMap,legend=FALSE) }) when it is changed to leaflet::renderLeaflet({mapview(SomeMap,legend=FALSE)@map }) it worked...

By the way, the legend false or true is just an additional option in the mapview.

MehrdadVaredi avatar Dec 29 '22 05:12 MehrdadVaredi