I am trying to create a map using leaflet in which interacting with a selectInput modifies the data loaded in the map. I think I'm doing the filtering right and using the reactive variables but I keep getting this error:
Error in colnames(sum_crimes()) <- c("area_numbe", "total_count") :
invalid (NULL) left side of assignment
It seems that 'filtered_crimes' or 'sum_crimes' are always empty and I can't understand why. I would be grateful if you could help me.
This is the code of the application:
library(shiny)
library(leaflet)
library(leaflet.extras)
library(geojsonio)
library(dplyr)
library(shinycssloaders)
# Carga el archivo Chicago_Crimes_2015-2021.csv
crimes <- read.csv("Chicago_Crimes_2015-2021.csv")
# Load Polygons
community <- geojson_read("CommAreas.geojson", what = "sp")
community$Bold <- paste0('<strong>', community$community, '</strong>') %>%
lapply(htmltools::HTML)
ui <- fluidPage(
title = "Chicago Map",
headerPanel(h4("Mapa de crÃmenes en Chicago")),
sidebarLayout(
sidebarPanel(
selectInput("crime_type", "Crime Type:", c("All", unique(crimes$Primary.Type)), selected = "All")
#style = "overflow-y:scroll; max-height: 70vh; position:relative;"
),
mainPanel(withSpinner(leafletOutput("map", height = "90vh")))
)
)
server <- function(input, output) {
filtered_crimes <- reactive({
if (input$crime_type == "All") {
crimes
} else {
crimes %>% filter(Primary.Type == input$crime_type)
}
})
sum_crimes <- reactive({
filtered_crimes() %>% group_by(Community.Area) %>%
summarise(total_count=n(),
.groups = 'drop')
})
colnames(sum_crimes()) <- c("area_numbe", "total_count")
max_crimes <- max(sum_crimes()$total_count)
min_crimes <- min(sum_crimes()$total_count)
# Creating a sequential color map
pal <- colorNumeric("YlOrBr", sum_crimes()$total_count)
output$map <- renderLeaflet({
leaflet(options = leafletOptions(minZoom = 10, preferCanvas = TRUE)) %>%
addProviderTiles("CartoDB.DarkMatter", group = "Dark Theme") %>%
addProviderTiles("CartoDB.Positron", group = "Light Theme") %>%
setView(lng = -87.654231, lat = 41.877562, zoom = 5) %>%
setMaxBounds(lng1 = -87.455032, lat1 = 41.6, lng2 = -87.955673, lat2 = 42.040927) %>%
addPolygons(data = community, fillOpacity = 0.8, color = "#0e80c2", fillColor = ~pal(sum_crimes()[area_numbe,]$total_count),
label = paste0('<strong>', community$community, '</strong><br/>Num. crimes: ', sum_crimes()[community$area_numbe,]$total_count) %>%
lapply(htmltools::HTML),
highlightOptions = highlightOptions(color = "#A9A9A9", opacity = 1, weight = 8, stroke = 8),
labelOptions = labelOptions(textsize = "13px"))%>%
# creating a legend
addLegend(data = sum_crimes, pal = pal, values = ~total_count, opacity = 1.0, title = "Number of crimes")
})
}
shinyApp(ui, server)
I tried with an if-else to initialise the data with a defined value in case it is empty at the beginning but it didn't work either.
This is the structure of crimes. Since it has a large amount of data, it only paints the equivalent of head(crimes):
structure(list(X = 0:5, ID = c(10224738L, 10224739L, 11646166L,
10224740L, 10224741L, 10224742L), Case.Number = c("HY411648",
"HY411615", "JC213529", "HY411595", "HY411610", "HY411435"),
Date = c("09/05/2015 01:30:00 PM", "09/04/2015 11:30:00 AM",
"09/01/2018 12:01:00 AM", "09/05/2015 12:45:00 PM", "09/05/2015 01:00:00 PM",
"09/05/2015 10:55:00 AM"), Block = c("043XX S WOOD ST", "008XX N CENTRAL AVE",
"082XX S INGLESIDE AVE", "035XX W BARRY AVE", "0000X N LARAMIE AVE",
"082XX S LOOMIS BLVD"), IUCR = c("0486", "0870", "0810",
"2023", "0560", "0610"), Primary.Type = c("BATTERY", "THEFT",
"THEFT", "NARCOTICS", "ASSAULT", "BURGLARY"), Description = c("DOMESTIC BATTERY SIMPLE",
"POCKET-PICKING", "OVER $500", "POSS: HEROIN(BRN/TAN)", "SIMPLE",
"FORCIBLE ENTRY"), Location.Description = c("RESIDENCE",
"CTA BUS", "RESIDENCE", "SIDEWALK", "APARTMENT", "RESIDENCE"
), Arrest = c("False", "False", "False", "True", "False",
"False"), Domestic = c("True", "False", "True", "False",
"True", "False"), Beat = c(924L, 1511L, 631L, 1412L, 1522L,
614L), District = c(9, 15, 6, 14, 15, 6), Ward = c(12, 29,
8, 35, 28, 21), Community.Area = c(61, 25, 44, 21, 25, 71
), FBI.Code = c("08B", "06", "06", "18", "08A", "05"), X.Coordinate = c(1165074,
1138875, NA, 1152037, 1141706, 1168430), Y.Coordinate = c(1875917,
1904869, NA, 1920384, 1900086, 1850165), Year = c(2015L,
2015L, 2018L, 2015L, 2015L, 2015L), Updated.On = c("02/10/2018 03:50:01 PM",
"02/10/2018 03:50:01 PM", "04/06/2019 04:04:43 PM", "02/10/2018 03:50:01 PM",
"02/10/2018 03:50:01 PM", "02/10/2018 03:50:01 PM"), Latitude = c(41.815117282,
41.895080471, NA, 41.937405765, 41.881903443, 41.744378879
), Longitude = c(-87.669999562, -87.765400451, NA, -87.716649687,
-87.755121152, -87.658430635), Location = c("(41.815117282, -87.669999562)",
"(41.895080471, -87.765400451)", "", "(41.937405765, -87.716649687)",
"(41.881903443, -87.755121152)", "(41.744378879, -87.658430635)"
)), row.names = c(NA, 6L), class = "data.frame")
And this about community:
{
"type": "FeatureCollection",
"features": [
{"type":"Feature","properties":{"community":"DOUGLAS","area":"0","shape_area":"46004621.1581","perimeter":"0","area_num_1":"35","area_numbe":"35","comarea_id":"0","comarea":"0","shape_len":"31027.0545098"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-87.60914087617894,41.84469250265398],[-87.60914874757808,41.84466159842403],[-87.6091611204126,41.84458961193954],
area_numbe and Community.Area is the column shared by both datasets. Previously I tried to merge sum_crimes and community but when I did it community was no longer a spatial dataframe and addPolygons failed.
Without an extract of your data, it is difficult to make sure where the errors are but I think it is caused by altering a reactive outside of a reactive environment.
Instead of
sum_crimes <- reactive({
filtered_crimes() %>% group_by(Community.Area) %>%
summarise(total_count=n(),
.groups = 'drop')
})
colnames(sum_crimes()) <- c("area_numbe", "total_count")
You should do
sum_crimes <- reactive({
filtered_crimes() %>% group_by(Community.Area) %>%
summarise(total_count=n(),
.groups = 'drop') %>%
rename(area_numbe = Community.Area)
})
# delete the line colnames(sum_crimes()) <- ...
Same goes for
max_crimes <- max(sum_crimes()$total_count)
min_crimes <- min(sum_crimes()$total_count)
Those 2 lines should go in a reactive environment if you want to use max_crimes and min_crimes later. You could do something like this :
# initialize reactiveValues
rv <- reactiveValues(max_crimes = NA, min_crimes = NA)
observeEvent(sum_crimes(), {
rv$max_crimes <- max(sum_crimes()$total_count)
rv$min_crimes <- min(sum_crimes()$total_count)
})
And then use rv$max_crimes
and rv$min_crimes
later in your code when needed.
But once again, without data I cannot run your code so this is maybe not accurate to your needs.
To add your data to your post, you can do dput(crimes)
in R console and copy paste the result in your original post.
EDIT : same goes for your line pal <- colorNumeric("YlOrBr", sum_crimes()$total_count)
, you should put this line at the beginning of the renderLeaflet
for example