Search code examples
rshinyr-leafletshiny-reactivityrgdal

Reactive reading and rendering a shapefile


my purpose is to render a reactive map through Shiny + Leaflet: I want to use two overlapped layers, "confini.comuni.WGS84" and "confini.asl.WGS84", on which to draw a reactive layer.

Based on the value 'inputId = "Year.map"', the server reads a layer 'zone.WGS84' ('layer = paste0 ("zone_", anno.map ())', EX "zone_2015") and colors the polygons based on the value one of the fields in the dataframe ("SIST_NERV", "MESOT", "TUM_RESP") selected via 'inputId = "Pathology.map"'.

The shapefiles "zone_2000.shp" etc.. are stored in "App/shapes/zone", the shapefiles "rt.confini.comunali.shp" and "rt.confini.regionali.shp" are stored in "App/shapes/originali"

The App and the files are here:

The data.frame related to the shapesfile "zone_2016" is:

 EXASLNOME                     Anno SIST_NERV SIST_NERVp MESOT MESOTp TUM_RESP TUM_RESPp
 Az. USL 1 di Massa Carrara    2016        43         41     1      1        4         4     
 Az. USL 2 di Lucca            2016        45         45    11     10        3         3
 Az. USL 3 di Pistoia          2016        26         21    13     13        5         5
 Az. USL 4 di Prato            2016         6          6     8      8       NA        NA
 Az. USL 5 di Pisa             2016       155        146     3      3        2         2
 Az. USL 6 di Livorno          2016       137        136    17     17       20        18
 Az. USL 7 di Siena            2016        29         24     1      1       NA        NA
 Az. USL 8 di Arezzo           2016        31         29     3      3        2         2
 Az. USL 9 di Grosseto         2016        35         34     2      2        1         1
 Az. USL 10 di Firenze         2016        34         33    24     13       11         4
 Az. USL 11 di Empoli          2016        30         29     2      2       20        20
 Az. USL 12 di Viareggio       2016       130        129     7      7        3         3 

Next, Leaflet must create a reactive label built on the data 'EXASLNOME' and 'pat.map()' of the data.frame. Finally, a map() map must be generated via renderLeaflet sent to output$Map.ASL. This generates this error:

Warning: Error in domain: could not find function "domain" Stack trace (innermost first): 91: colorQuantile 90: [C:/Users/User/Downloads/Prova_mappe/App_per_Stackoverflow.r#63] 79: mappa 78: func [C:/Users/User/Downloads/Prova_mappe/App_per_Stackoverflow.r#95] 77: origRenderFunc 76: output$Mappa.ASL 1: runApp

I can not use all the reactive components to pass as parameters to the Leaflet function, can you tell me something?

  require(shiny)
  require(stringr)
  require(shinythemes)
  require(leaflet)
  require(RColorBrewer)
  require(rgdal)
  require(rgeos)

  #### UI ####
  ui <- fluidPage(
    theme = shinytheme("spacelab"),
    titlePanel("Indice"),
    navlistPanel( 
      tabPanel(title = "Mappe",
         fluidRow(column(6, sliderInput(inputId = "Anno.map",
                                        label = "Anno di manifestazione",
                                        min = 2000,
                                        max = 2016, 
                                        value = 2016,
                                        step = 1,
                                        ticks = FALSE,
                                        sep = "")),
                  column(6, selectInput(inputId = "Patologia.map",
                                        label = "Patologia",
                                        choices = list("SIST_NERV", "MESOT","TUM_RESP"),
                                        selected = "SIST_NERV",
                                        multiple = FALSE))),
         fluidRow(column(6, leafletOutput(outputId = "Mappa.ASL", height = "600px", width = "100%")))
    )
   )
  )

 #### SERVER ####
 server <- function(input, output) {

    # NOT REACTIVE 
    confini.comuni <- readOGR(dsn = "shapes/originali", layer = "rt.confini.comunali", stringsAsFactors = FALSE)
    confini.comuni.WGS84 <- spTransform(confini.comuni, CRS("+proj=longlat +datum=WGS84 +no_defs")) 

    confini.asl <- readOGR(dsn = "shapes/originali", layer = "rt.confini.asl", stringsAsFactors = FALSE)
    confini.asl.WGS84 <- spTransform(confini.asl, CRS("+proj=longlat +datum=WGS84 +no_defs"))

    # REACTIVE 
    anno.map <- reactive({input$Anno.map})

    pat.map <- reactive({input$Patologia.map})

    mappa <- reactive({                                                         
        zone.WGS84 <- spTransform(readOGR(dsn = "shapes/zone", 
                                  layer = paste0("zone_", anno.map()), stringsAsFactors = FALSE), 
                                  CRS("+proj=longlat +datum=WGS84 +no_defs"))           

        domain <- paste0("zone_", anno.map(), "@data$", pat.map())
        labels.1 <- paste0("zone_", anno.map(), "@data$EXASLNOME")
        labels.2 <- paste0("zone_", anno.map(), "@data$", pat.map())
        labels.3 <- paste0("zone_", anno.map(), "@data$", pat.map(), "p")

        pal <- colorQuantile(palette = "YlOrRd",  
                             domain = domain(), n = 6,
                             na.color = "808080", alpha = FALSE, reverse = FALSE, right = FALSE)
        labels <- sprintf("<strong>%s</strong><br/>%g Segnalazioni<br/> %g con nesso positivo",
                   labels.1(), labels.2(), labels.3()) %>% 
                   lapply(htmltools::HTML)    

    leaflet(options = leafletOptions(zoomControl = FALSE, dragging = FALSE, minZoom = 7.5, maxZoom = 7.5)) %>%   
            addPolygons(data = confini.comuni.WGS84,
            weight = 1,
            opacity = 1,
            color = "black") %>%
    addPolygons(data = confini.asl.WGS84,
                weight = 2,
                opacity = 1,
                color = "red")  %>%      
    addPolygons(data = zone.WGS84(), 
                fillColor = ~pal(domain()),
                weight = 2,
                opacity = 1,
                color = "white",
                dashArray = "3",
                fillOpacity = 0.7,
                highlight = highlightOptions(weight = 5,
                                             color = "666",
                                             dashArray = "",
                                             fillOpacity = 0.7,
                                             bringToFront = TRUE),
                label = labels())
    })


   output$Mappa.ASL <- renderLeaflet({mappa()})

  }

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

Solution

  • There were several mistakes in your code, the missing labels were just a minor problem.

    First of all, you can put all non reactive values outside the server function and maybe you should save the confini.* shapefiles to an RDS-file or a DB and load them from there. I guess that would speed up your App.


    Your leaflet plot was never showing, because you rendered the object mappa() to the output ID = Mappa.ASL. The reactive mappa doesnt create a map though, its not returning a map or any object, so you should change the reactive to an observer. The LeafletProxy just adds stuff on the original map (in your case mappa.base), which you never used in the UI.


    Your error came from calling labels = labels() in addPolygons, as if labels was a reactive object, but you defined it in the same reactive environment so you call it without parenthesis like:

    labels = labels


    Instead of making a reactive value out of those:

    anno.map <- reactive({input$Anno.map})
    pat.map <- reactive({input$Patologia.map})
    pat.map.p <- reactive({paste0(pat.map(), "p")})
    

    You can just use them as reactives like:

    input$Anno.map
    input$Patologia.map
    paste0(pat.map(), "p")
    

    I also wouldnt use a reactive (map) which always reads a shapefile from disk and reprojects it straight away. Can you maybe merge them together to one shapefile and then filter from it and reproject them beforehand, so you dont have to do it everytime the app is called?

    The following app should work. At least a bit, as you will run in errors in the colorQuantile function like this one, as there are NA-values in the datasets (eg. years 2009-2006 for 'SIST_NERV')

    Warning: Error in cut.default: 'breaks' are not unique

    You could just change the colorQuantile to colorBin and drop the n = 6 argument.

    require(shiny)
    require(stringr)
    require(shinythemes)
    require(leaflet)
    require(RColorBrewer)
    require(rgdal)
    require(rgeos)
    
    
    # NOT REACTIVE 
    confini.comuni <- readOGR(dsn = "shapes/originali", layer = "rt.confini.comunali", stringsAsFactors = FALSE)
    confini.comuni.WGS84 <- spTransform(confini.comuni, CRS("+proj=longlat +datum=WGS84 +no_defs"))
    
    confini.zone <- readOGR(dsn = "shapes/originali", layer = "rt.confini.exasl", stringsAsFactors = FALSE)
    confini.zone.WGS84 <- spTransform(confini.zone, CRS("+proj=longlat +datum=WGS84 +no_defs"))
    
    confini.asl <- readOGR(dsn = "shapes/originali", layer = "rt.confini.asl", stringsAsFactors = FALSE)
    confini.asl.WGS84 <- spTransform(confini.asl, CRS("+proj=longlat +datum=WGS84 +no_defs"))
    
    
    #### UI ####
    ui <- {fluidPage(
      theme = shinytheme("spacelab"),
      titlePanel("Indice"),
      navlistPanel( 
        tabPanel(title = "Mappe",
                 fluidRow(column(6, sliderInput(inputId = "Anno.map",
                                                label = "Anno di manifestazione",
                                                min = 2000, max = 2016, value = 2016, step = 1,
                                                ticks = FALSE, sep = "")),
                          column(6, selectInput(inputId = "Patologia.map",
                                                label = "Patologia", choices = list("SIST_NERV", "MESOT","TUM_RESP"),
                                                selected = "SIST_NERV", multiple = FALSE))),
                 fluidRow(column(6, 
                                 leafletOutput(outputId = "mappa.base", height = "600px", width = "100%")
                                 ))
        )
      )
    )}
    
    
    #### SERVER ####
    server <- function(input, output) {
    
      # REACTIVE 
      map <- reactive({
        req(input$Anno.map)
        spTransform(readOGR(dsn = "shapes/zone", layer = paste0("zone_", input$Anno.map), stringsAsFactors = FALSE),
                    CRS("+proj=longlat +datum=WGS84 +no_defs"))
      })
    
      output$mappa.base <- renderLeaflet({
        leaflet(options = leafletOptions(zoomControl = FALSE, dragging = FALSE, 
                                         minZoom = 7.5, maxZoom = 7.5)) %>%   
          addTiles() %>% 
          addPolygons(data = confini.comuni.WGS84,
                      weight = 1, opacity = 1, color = "black") %>%
          addPolygons(data = confini.zone.WGS84,
                      weight = 2, opacity = 1, color = "black")
      })
    
    
      map.df <- reactive({
        req(input$Anno.map)
        map() %>%
          as.data.frame() %>%
          dplyr::select(EXASLNOME, input$Patologia.map, paste0(input$Patologia.map, "p"))
      })
    
      mappa <- observe({
        pal <- colorQuantile(palette = "YlOrRd",  domain = map.df()[,2],
                             n = 6,  na.color = "808080",
                             alpha = FALSE, reverse = FALSE,
                             right = FALSE)
    
        labels <- sprintf("<strong>%s</strong> <br/> %d Segnalazioni <br/> %d con nesso positivo",
                          map.df()[,1], map.df()[,2], map.df()[,3]) %>% lapply(htmltools::HTML)
    
        leafletProxy(mapId = "mappa.base", data = map()) %>%
          addPolygons(fillColor = ~pal(map.df()[,2]),
                      weight = 2,
                      opacity = 1,
                      color = "white",
                      dashArray = "3",
                      fillOpacity = 0.7,
                      highlight = highlightOptions(weight = 5,
                                                   color = "666",
                                                   dashArray = "",
                                                   fillOpacity = 0.7,
                                                   bringToFront = TRUE),
                      label = labels
          )
      })
    }
    
    # Run the application 
    shinyApp(ui = ui, server = server)