You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()数据,不仅效率低,还容易匹配错误,且未处理无犯罪记录的社区。

修正方案

关键调整点

  1. 将列名修改逻辑整合到sum_crimes反应式内部,直接在统计时指定匹配空间数据的列名。
  2. 所有依赖反应式数据的变量(如调色板)都放在renderLeaflet内部或包装为反应式对象,确保随输入更新。
  3. 使用sp::merge合并统计数据与空间数据,保留空间属性,避免addPolygons失效。
  4. 处理无犯罪记录的社区,填充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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 22:25:26