Shiny中用selectInput实现响应式Leaflet地图的报错排查
问题分析与解决
错误原因
你遇到的Error in colnames(sum_crimes()) <- c("area_numbe", "total_count") : invalid (NULL) left side of assignment错误,核心问题有三个:
- 反应式变量操作时机错误:
sum_crimes()是反应式对象,你在反应式上下文(如render*或reactive函数)外部直接修改它的列名,此时反应式变量还未触发计算,返回值为NULL,导致赋值失败。 - 非反应式变量依赖反应式数据:
max_crimes、min_crimes、pal这些变量依赖反应式的sum_crimes(),但被定义在反应式上下文之外,既不会随输入更新,初始化时也会因sum_crimes()未计算而报错。 - 空间数据关联方式错误:直接在
addPolygons里通过索引匹配sum_crimes()数据,不仅效率低,还容易匹配错误,且未处理无犯罪记录的社区。
修正方案
关键调整点
- 将列名修改逻辑整合到
sum_crimes反应式内部,直接在统计时指定匹配空间数据的列名。 - 所有依赖反应式数据的变量(如调色板)都放在
renderLeaflet内部或包装为反应式对象,确保随输入更新。 - 使用
sp::merge合并统计数据与空间数据,保留空间属性,避免addPolygons失效。 - 处理无犯罪记录的社区,填充
total_count为0,避免渲染错误。
修正后的完整代码
library(shiny) library(leaflet) library(leaflet.extras) library(geojsonio) library(dplyr) library(shinycssloaders) # 加载数据 crimes <- read.csv("Chicago_Crimes_2015-2021.csv") community <- geojson_read("CommAreas.geojson", what = "sp") 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") ), 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') %>% rename(area_numbe = Community.Area) }) # 合并空间数据与统计数据,保留空间属性,处理无犯罪社区 merged_community <- reactive({ # 生成包含所有社区的基础数据框 all_areas <- data.frame(area_numbe = as.integer(community$area_numbe)) full_sum <- all_areas %>% left_join(sum_crimes(), by = "area_numbe") %>% mutate(total_count = ifelse(is.na(total_count), 0, total_count)) # 合并到空间数据框,保留空间属性 sp::merge(community, full_sum, by = "area_numbe") }) output$map <- renderLeaflet({ # 基于当前统计数据创建调色板 pal <- colorNumeric("YlOrBr", merged_community()$total_count) 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 = 10) %>% # 调整初始缩放级别至芝加哥区域 setMaxBounds(lng1 = -87.455032, lat1 = 41.6, lng2 = -87.955673, lat2 = 42.040927) %>% addPolygons( data = merged_community(), fillOpacity = 0.8, color = "#0e80c2", fillColor = ~pal(total_count), # 直接使用合并后的列 label = ~paste0('<strong>', community, '</strong><br/>Num. crimes: ', total_count) %>% lapply(htmltools::HTML), highlightOptions = highlightOptions(color = "#A9A9A9", opacity = 1, weight = 8, stroke = 8), labelOptions = labelOptions(textsize = "13px") ) %>% addLegend( pal = pal, values = merged_community()$total_count, opacity = 1.0, title = "Number of crimes" ) }) } shinyApp(ui, server)
额外说明
- 初始缩放优化:原代码
zoom=5显示范围过大,改为zoom=10更适配芝加哥区域展示。 - 无犯罪社区处理:通过
left_join确保所有社区都有统计数据,无犯罪社区填充为0,避免地图空白。 - 反应式依赖同步:所有依赖输入的计算都放在反应式或
renderLeaflet内部,确保输入变化时相关组件同步更新。
内容的提问来源于stack exchange,提问作者James_00
相关产品推荐
相关产品推荐

