I am working on a R Shiny app. The user uploads his data file and it creates leaflet maps of the median values. The difficulty is that I need to generate maps for each variable, but that number can be different between each file.
The data is a reactive sf tibble, so providing a reprex is going to be difficult.
The problem is that only the last variable is being generated multiple times.
# server.R
function(input, output, session){
# Genrate fluidRow boxes for the maps
output$all_cartes_nutri_dept <- renderUI({
req(bilans_nutriments$dept())
# Init a "table" for the maps
tableau_cartes <- list()
# nutriments to check for validity
nutriments_mesures_to_check <- paste0(colonnes_disponibles$nutriments, "_med")
# Init of valid nutriments and map names
nutriments_valides <- c()
noms_cartes_nutri_dept <- c()
# Loop over each valid nutriment and prepare the output box
for (i in seq_along(nutriments_mesures_to_check)) {
# Verifier si la colonne est valide dans bilans_nutriments
if (check_colonne(nutriments_mesures_to_check[i], bilan_nutri_dept_shape())) {
# Add valid nutriment and map names to their respectives vectors
nutriments_valides <- c(nutriments_valides, colonnes_disponibles$nutriments[i])
noms_cartes_nutri_dept <- c(noms_cartes_nutri_dept, paste0("carte_nutri_dept_", colonnes_disponibles$nutriments[i]))
# Add valid map box to the table of maps
tableau_cartes[[length(tableau_cartes) + 1]] <- box(
withSpinner(leafletOutput(noms_cartes_nutri_dept[i]), color = spinner_color)
)
} else {
message_nodata <- paste("No data for nutriment:", nutriments_mesures_to_check[i])
print(message_nodata)
}
}
print("Nutriments valides :")
print(nutriments_valides)
print("Noms des cartes :")
print(noms_cartes_nutri_dept)
output$message_nodata <- renderText({
message_nodata
})
# Créer les cases du tableau avec les boîtes pour chaque carte
fluid_row_output <- list()
# Pour chaque element du tableau
for (i in seq(1, length(noms_cartes_nutri_dept), by = 2)) {
fluid_row_output[[length(fluid_row_output) + 1]] <- fluidRow(
tableau_cartes[[i]],
if (i + 1 <= length(tableau_cartes)) tableau_cartes[[i+1]] else NULL
)
}
# Generer les cartes pour chaque nutriment
print("Génération des cartes pour chaque nutriment")
for (i in seq_along(noms_cartes_nutri_dept)) {
print(paste("Génération de la carte pour le nutriment :", nutriments_valides[i], "avec le nom :", noms_cartes_nutri_dept[i]))
# TO FIX 1 ----
# this is where the error appears. The above print gives me a list of length 7 with the correct values.
# Then, I pass the same arguments to the output and drawing map function below
# Bug 1 : it only gives the last element of the vector to generer_carte_nutri_dept() instead of all 7 elements (length(noms_cartes_nutri_dept) = 7)
# Bug 2 : it only gives the last element of the vector 2 times, instead of looping 7 times
output[[noms_cartes_nutri_dept[i]]] <- renderLeaflet({
# TO FIX 2 ----
# And here it breaks. Instead of getting each element of nutriment_valides, it only receives the last element of the vector.
print(i) # only print 7 twice instead of 1 2 3 4 5 6 7
carte_bilan_departemental(
df_bilan_shape = bilan_nutri_dept_shape(),
produit = input$selecteur_MP,
nutriment = nutriments_valides[i],
mesure = "med"
)
})
}
return(do.call(tagList, fluid_row_output))
})
body <- dashboardBody(
tabItems(
tabItem(
# Génération dynamiques des fluidRows
uiOutput("all_cartes_nutri_dept")
)
)
)
# Dummy Data for Nutrient Information and Departments
set.seed(123)
# Departments (for simulation purposes, can be any identifier)
departments <- c("Department A", "Department B", "Department C", "Department D")
# Simulate some nutritional data for 4 departments and 3 nutrients
df_bilan <- data.frame(
nom_produit = rep(c("Product 1", "Product 2"), each = 4),
nom = rep(departments, times = 2), # Department names
humidite_med = runif(8, 10, 30), # Random values for the nutrient
proteines_med = runif(8, 2, 15), # Random values for the nutrient
lipides_med = runif(8, 5, 20) # Random values for the nutrient
)
# Simulate available nutrients
colonnes_disponibles <- list(
nutriments = c("humidite", "proteines", "lipides")
)
# Simulated product selection (input)
input <- list(selecteur_MP = "Product 1") # Can be any valid product
Any ideas how to solve TO FIX 1
and TO FIX 2
. I think that the only issue is that the correct variables are not being fed to generer_carte_nutri_dept()
.
I learned too late about modules to include them in this app so please don't suggest it at this stage. But it's in the works for later.
EDIT : I moved the code generating the leaflet map inside the main output logic. Still bugged.
I fixed it using the answer to that question : R Shiny renderPlot not respecting parent environment in for loop. The problem was the lazy evaluation of one the variables.
By replacing the for loop with an apply-like function, it fixed the issue.
for (i in seq_along(noms_cartes_nutri_dept)) {
output[[noms_cartes_nutri_dept[i]]] <- generer_carte_nutri_dept(nutriments_valides[i])
}
map(seq_along(noms_cartes_nutri_dept), function(i) {
output[[noms_cartes_nutri_dept[i]]] <- generer_carte_nutri_dept(nutriments_valides[i])
})