r-spatial / mapview

Interactive viewing of spatial data in R
https://r-spatial.github.io/mapview/
GNU General Public License v3.0
516 stars 91 forks source link

Problem with dynamic map in Shiny app #254

Open kent37 opened 4 years ago

kent37 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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

kent37 commented 4 years ago

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)
}
lbusett commented 4 years ago

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).

kent37 commented 4 years ago

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

pat-s commented 4 years ago

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

The renderLeaflet() @map workaround works for me.

rvalavi commented 2 years ago

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

MehrdadVaredi commented 1 year ago

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.