3
votes

I am using Shiny and i have an issue with Leaflet.

What I want to do : The ultimate goal of my project is to select specific french counties. When those counties are selected, I create on-the-go (by merging) the file of shapes (that is necessary to plot the map on Leaflet) and the database containing all kind of public data (such as number of population, ect...) that correspond only to those specific counties.

My Issue : I know that the "merging" is done well, inside the Shiny App. BUT, my Outputmap doesn't want to work.

Details: Actually, I launched my "merging and plotting" on another .R script, and it work perfectly well (i only defined "manually" before launching the script what counties i wanted to plot). On my ShinyApp, this choice is taken by the input$choix_depa variable.

Now a little bit of code. I have three scripts that react simultaneously : global.R ui.R and server.R (this is how Shiny works as you know). For the global.R (i only show here the"interessant" part) i load the database file containing the data for all counties (not shape files ! only data)

setwd('path')
data_BP = read_delim(
  "database-allFrance.csv",
  ",",
  na = "empty",
  quote = "\"",
  locale = locale(encoding = 'windows-1252')
)

On the ui.R (user interface) I have my "selection" of counties :

shinydashboard::tabItem(tabName= "Departements", class = 'active',
           shiny::fluidPage(
           shiny::absolutePanel(
           draggable = FALSE,
           fixed = TRUE, 
           top = 60, left = "auto", right = 20, bottom = "auto",
           width = 330, height = "auto",
           wellPanel(
           shiny::h4("Départements"),
           selectizeInput(inputId = "choix_depa", label = "",multiple=TRUE,
           choices = seq(1,95))
            )
            ), textOutput("answ")
            ),

The selectizeInput is the button that allows the user to choose one or more counties in the seq(1,95).

And on the server.R (most important) I have:

ObserveEvent(input$choix_depa, {
    output$answ<- renderText({
      paste("You choose", input$choix_depa)
    })
    choice=input$choix_depa
    print(choice)
    for (i in input$choix_depa){
      setwd(sprintf("path/county%s",i))

      assign(paste("contouriris",i,sep=""), readOGR(
        dsn = "contours_IRIS_BP.shp",
        layer = "contours_IRIS_BP",
        verbose = FALSE,
        encoding = 'UTF-8'
      ))
      print("modification en cours...")
      assign(paste("data_BP",i,sep=""),subset(data_BP,as.numeric(as.character(data_BP$IRIS))>=as.numeric(i)*10000000&as.numeric(as.character(data_BP$IRIS))<(as.numeric(i)+1)*10000000))

    }

    if (length(input$choix_depa)>=1){
      contours_IRIS  <- get(paste("contouriris",input$choix_depa[1],sep=""))
      data_BPC <- get(paste("data_BP",input$choix_depa[1],sep=""))
    }
    if (length(input$choix_depa)>1){
      for (i in input$choix_depa[-1]){
        contours_IRIS <- rbind(contours_IRIS,get(paste("contouriris",i,sep="")))
        data_BPC <- rbind(data_BPC,get(paste("data_BP",i,sep="")))
      }


    }
    map_WGS84 = spTransform(
      merge(contours_IRIS, data_BPC, by.x = 'CODE_IRIS', by.y = 'IRIS'),
      CRS("+init=epsg:4326")
    ) 
    # Correction of names :
    names(map_WGS84)[names(map_WGS84) == "TYP_IRIS.x"] <- "TYP_IRIS"
  })

You don't have to understand all this code. You have the choice of counties in the input$choix_depa variable. This kind of variable is like ["4',"87"] if the counties 4 and 87 are selected by the user inside the app (for example). I have on my computer 95 folders (one for each county). I use setwd to go to the right path, and I load the "shape file" in the contouririsK with K the number of the county. In the previous example we would have contouriris4 and countouriris87. Those shape files are merged in contours_IRIS. I do the same thing with the data file, i took the data that is associated with those counties and i merge all the files in the data_BPC. (if i take the previous example, we would have data_BPC4 and data_BPC87 that are merged in the data_BPC).

After this, i use both variables(contours_IRIS and data_BPC) to create the map_WGS84 variable (i will need this variable for the LeafletOutput) .

OK so, after this choice of counties that i want to plot on the Leaflet map I have to choose the variables in which i am interested in. This is another menu, i don't write here all the code (not necessary)

observeEvent(input$choix_var_pop, {XXXXXXXXX})

The choice of the variable that the user want to plot on the map is in input$choix_var_pop . After this, i create specific variables that i will need on my LeafletMap :

label_reac_pop = reactive({as.character(input$choix_var_pop)})
var_reac_pop = reactive({dico$Variable[dico$Label == label_reac_pop()]})
col_reac_pop = reactive({as.character(dico$Couleur[dico$Label == label_reac_pop()])})
type_reac_pop = reactive({as.character(dico$Type[dico$Label == label_reac_pop()])})
unite_reac_pop = reactive({ifelse(as.character(type_reac_pop()) == "Pct", " %", "")})

Lastly, i plot the LeafletMap: (i have greatly reduced for clarity the code below)

output$Carte_Pop <- renderLeaflet({

      label = label_reac_pop() 
      var = var_reac_pop() 
      col = col_reac_pop() 
      type = type_reac_pop() 
      unite = unite_reac_pop() 
      values_var = map_WGS84@data[,var] 

      leaflet(map_WGS84) %>%
      addProviderTiles("CartoDB.PositronNoLabels") %>% 
      addProviderTiles("CartoDB.PositronOnlyLabels") %>% 
      setView(lng = 2.468738900000062, lat = 49.19316, zoom = 7)  %>% 
      clearShapes() %>%                       
      clearPopups() %>% 
  })

Of course, i call this output$Carte_Pop in the ui.R file, in order to plot it. OKAY so, what is the result of all of this ? As I mentionned before, this script work when it is "alone", and when there is no input$choix_depa (i manually enter the counties that i want to merge in an array, and they are merged well, and the map is well plotted). BUT when i am on the ShinyApp with my 3 scripts (global.R, ui.R and server.R), the "new" value of the "map" is not "saved".

For example: if i choose (on my alone-script) to merge and plot counties number 4 and 87, it works fine (the merging part and the plotting part work well) !

BUT when i launch my ShinyApp, when i choose the counties that i want (for example 13 and 91 ), EVEN IF the contours_IRIS and the data_BPC are well merged with the data corresponding to 13 and 91, so i suppose that the map_WGS84 that is created INSIDE the observeEvent(input$choix_depa....) is CORRESPONDING WELL to 13 and 91, when I ask for plotting a specific variable (after the observeEvent(input$choix_var_pop) the map that is plotted IS NOT the map created before, but the "older MAP" with 4 and 87 (the map that was created on the "alone-script"... before launching the ShinyApp !). But i am sure at 100% that the MAP created inside of the observeEvent(input$choix_depa .... ) is good. But, the "value" of this MAP is not "saved" by ShinyApp (they use instead the old value for the MAP...).

So my question is : what should i do in order to plot the GOOD NEW MAP (created inside the APP) instead of the OLD BAD ONE (created BEFORE and OUTSIDE the app...) ?

This issue is a little bit "complex", if you have any question please feel free to ask !

Thank you ! :)

2

2 Answers

3
votes

Summary: When you want to make some output dependent on other expressions, you should use an eventReactive, or store the object in an reactiveValues and update that from an observeEvent.

Okay, I've read your question and here are my thoughts:


ObserveEvent(input$choix_depa, {
   output$answ<- renderText({
      paste("You choose", input$choix_depa)
    })

This is bad practice. observers should only be used for side effects, not for creating output. That should become:

   output$answ<- renderText({
      paste("You choose", input$choix_depa)
    })

ObserveEvent(input$choix_depa, {
....

renderText will also fire whenever input$choix_depa changes, since it is reactive on that. So no need to place it in the observer.


map_WGS84 = spTransform(
  merge(contours_IRIS, data_BPC, by.x = 'CODE_IRIS', by.y = 'IRIS'),
  CRS("+init=epsg:4326")
) 

What kind of object is map_WGS84? This will only work if it is a reactiveValue. Otherwise you are not overwriting the global variable, but just the local one within the function observeEvent. Once the observeEvent finishes, the global map_WGS84 has not changed. I guess this is the problem here.


The best option is probably to make this an eventReactive, instead of an observeEvent, since you want it to produce output that can be used elsewhere. The other option is to store map_WGS84 in a reactiveValues expression, and overwrite that from your observeEvent.

0
votes

Thank you Florian for your help, i will describe precisely how i succeeded:

 output$answ<- renderText({
      paste("Choix départements:", input$choix_depa)
  })

 observeEvent(input$choix_depa, {
      choice=input$choix_depa
  })
  map_reactive <- eventReactive(input$choix_depa,{
     ... merging and creating contours_IRIS (shape file) 
     and data_BPC given input$choix_depa ...

     map_WGS84 = spTransform(
        merge(contours_IRIS, data_BPC, by.x = 'CODE_IRIS', by.y = 'IRIS'),
        CRS("+init=epsg:4326")
        )

     list(map = map_WGS84) 
   })

observeEvent(input$choix_var_pop, {XXXXXXXXX})

... defining variables... 

output$Carte_Pop <- renderLeaflet({
      compulsive = map_reactive()$map
      label = label_reac_pop() 
      var = var_reac_pop() 
      col = col_reac_pop() 
      type = type_reac_pop() 
      unite = unite_reac_pop() 
      values_var = compulsive@data[,var] 

      leaflet(compulsive) %>%

  })

What was also "important" here was to add the "list" at the end of the eventReactive, in order to call the "map_reactive$map" later. Problem solved !