Brilliant (R) issue when updating tilemap inside app

I am using Shiny and I have a Leaflet problem.

What I want to do : The ultimate goal of my project is to select specific French districts. When these counties are selected, I create an on-the-go (by merging) form file (which is needed to build the map on a sheet) and a database containing all kinds of public data (e.g. population, etc ...). which only correspond to specific counties.

My question . I know the "merge" works well inside the Shiny application. BUT, my Outputmap doesn't want to work.

More details . Actually I ran my "merge and build" on a different .R script and it works fine (I only defined "manually" before running the script in which countries I wanted to build). On my ShinyApp, this selection is taken by a variable input$choix_depa

.

Now some code. I have three scripts that react at the same time: global.R ui.R and server.R (this is how Shiny works, as you know). For global.R (I'm only showing the "non-professional" part here), I load a database file containing data for all counties (not form files! Only data)

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

      

On ui.R (UI) I have my "pick" 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")
            ),

      

selectizeInput

- a button that allows the user to select one or more counties in seq(1,95)

.

And on the .R server (most importantly) 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 need to understand all this code. You have a selection of counties in a variable input$choix_depa

. This variable is similar to ["4", "87"] if environments 4 and 87 are selected by the user inside the application (for example). I have 95 folders on my computer (one for each county). I use setwd to navigate to the correct path, and load the "shape file" in contouririsK

, where K is the county number. In the previous example, we would have contouriris4

and countouriris87

. These form files are bundled into contours_IRIS

. I do the same with a data file, I took the data associated with those counties and merge all the files intodata_BPC

. (if I take the previous example, we would have data_BPC4

and data_BPC87

that were merged into data_BPC

).

After that, I use the two variables ( contours_IRIS

and data_BPC

) to create a variable map_WGS84

(I need this variable for LeafletOutput).

So, after this selection of the counties that I want to plot on the Sheets map, I need to select the variables in which I am interested. This is a different menu, I am not writing all the code here (optional)

observeEvent(input$choix_var_pop, {XXXXXXXXX})

      

The selection of the variable that the user wants to plot on the map is in input$choix_var_pop

. After that, I create the specific variables that I will need in my LeafletMap page:

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", " %", "")})

      

Finally, I draw the LeafletMap: (I have greatly reduced the code below for clarity)

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 $ Carte_Pop output in the ui.R file to build it. OKAY so what is the result of all this? As I mentioned earlier, this script works when it is "alone" and when it is not input$choix_depa

(I manually enter the districts I want to combine into an array and they are well combined and the map is well built). BUT when I am on 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 chose (my one is script) to merge and draw counties # 4 and 87, it works great (the merge part and the build part work well)!

BUT , when I run my ShinyApp, when I select the county that I need (eg, 13 and 91), EVEN IF contours_IRIS

and data_BPC

well integrated with the data corresponding to 13 and 91, therefore, assume that map_WGS84

created INSIDE observeEvent(input$choix_depa....)

, CORRESPONDING the WELL to 13 and 91 when I ask to build a specific variable (after the observeEvent(input$choix_var_pop)

displayed map is NOT the previously created map, but the "older MAP" with 4 and 87 (the map that was created in "alone-script" ... before running ShinyApp!). But I'm sure which is 100% that the MAP created internally observeEvent(input$choix_depa .... )

is good. But the "value" of this MAP is not "saved" by ShinyApp (they are instead the old value for MAP ...).

So my question is, what should I do to sketch a GOOD NEW MAP (created inside APP) instead of OLD BAD ONE (created BEFORE and EXTERNAL app ...)?

This problem is a bit “tricky”, if you have any questions feel free to ask!

Thank!:)

+3


source to share


2 answers


Summary. If you want some output to depend on other expressions, you must use eventReactive, or store the object in a reactive value and update it with the registerEvent function.

Ok I 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 generating output. This should become:

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

ObserveEvent(input$choix_depa, {
....

      



renderText will also fire whenever $ choix_depa changes, as it reacts to that. So there is 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 object is map_WGS84? This will only work if it is a reactive value. ... Otherwise, you are not overwriting the global variable, only the local one in the observEvent function. When the observEvent function finishes executing, the global map_WGS84 has not changed. I think this is the problem here.


The best option is probably to make it eventReactive rather than a watchdog function as you want it to generate output that can be used elsewhere. Another option is to store map_WGS84 in a reactiveValues ​​expression and overwrite it from your watcher.

+3


source


Thanks to Florian for your help, I'll tell you exactly 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" was to add a "list" at the end of the Reactive event to call map_reactive $ map later. The problem is solved!

0


source







All Articles