Spaces:
Sleeping
Sleeping
| # app.R | |
| library(shiny) | |
| library(dplyr) | |
| # library(lubridate) # 不要なので削除 | |
| library(leaflet) | |
| set.seed(123) | |
| # ========================= | |
| # 1. ダミーデータ作成 | |
| # ========================= | |
| # 工事現場(北海道の適当な範囲でランダム) | |
| sites <- tibble::tibble( | |
| site_id = 1:5, | |
| site_name = paste0("工事現場", 1:5), | |
| city = c("札幌市", "札幌市", "旭川市", "函館市", "帯広市"), | |
| lat = runif(5, 42.0, 44.5), # 北海道っぽい緯度 | |
| lng = runif(5, 141.0, 143.5), # 北海道っぽい経度 | |
| start = as.Date("2025-12-01"), | |
| end = as.Date("2025-12-31"), | |
| required_guards = c(3, 4, 2, 5, 3) | |
| ) | |
| cities <- sites$city | |
| # 警備員(住所は市レベルで、対応可能期間は12月中でバラバラ) | |
| guards <- tibble::tibble( | |
| guard_id = 1:20, | |
| guard_name = paste0("警備員", 1:20), | |
| city = sample(cities, 20, replace = TRUE), | |
| lat = runif(20, 42.0, 44.5), | |
| lng = runif(20, 141.0, 143.5), | |
| available_from = sample(seq(as.Date("2025-12-01"), as.Date("2025-12-15"), by = "day"), | |
| 20, replace = TRUE), | |
| available_to = sample(seq(as.Date("2025-12-16"), as.Date("2025-12-31"), by = "day"), | |
| 20, replace = TRUE) | |
| ) %>% | |
| # from <= to になるように補正 | |
| mutate( | |
| tmp_min = pmin(available_from, available_to), | |
| tmp_max = pmax(available_from, available_to), | |
| available_from = tmp_min, | |
| available_to = tmp_max | |
| ) %>% | |
| select(-tmp_min, -tmp_max) | |
| # ========================= | |
| # 2. UI | |
| # ========================= | |
| ui <- fluidPage( | |
| titlePanel("北海道・工事現場 × 警備員マッチング(ダミーデータ)"), | |
| sidebarLayout( | |
| sidebarPanel( | |
| dateInput( | |
| "selected_date", | |
| "日付を選択", | |
| value = as.Date("2025-12-01"), | |
| min = as.Date("2025-12-01"), | |
| max = as.Date("2025-12-31") | |
| ), | |
| checkboxInput("show_guards", "警備員の位置も地図に表示", TRUE), | |
| width = 3 | |
| ), | |
| mainPanel( | |
| leafletOutput("map", height = 500), | |
| br(), | |
| h4("工事現場別マッチング状況(選択日ベース)"), | |
| tableOutput("summary"), | |
| width = 9 | |
| ) | |
| ) | |
| ) | |
| # ========================= | |
| # 3. Server | |
| # ========================= | |
| server <- function(input, output, session) { | |
| # 選択日ごとのデータ | |
| daily_data <- reactive({ | |
| d <- input$selected_date | |
| # その日に稼働中の現場(期間内) | |
| active_sites <- sites %>% | |
| filter(start <= d, end >= d) | |
| # その日に勤務可能な警備員 | |
| available_guards <- guards %>% | |
| filter(available_from <= d, available_to >= d) | |
| list( | |
| date = d, | |
| active_sites = active_sites, | |
| available_guards = available_guards | |
| ) | |
| }) | |
| # マッチング集計(ここでは「同じ市にいる警備員数」で集計) | |
| summary_tbl <- reactive({ | |
| dd <- daily_data() | |
| active_sites <- dd$active_sites | |
| available_guards <- dd$available_guards | |
| guards_by_city <- available_guards %>% | |
| count(city, name = "available_guards") | |
| active_sites %>% | |
| left_join(guards_by_city, by = "city") %>% | |
| mutate( | |
| available_guards = ifelse(is.na(available_guards), 0L, available_guards), | |
| shortage = pmax(required_guards - available_guards, 0L) | |
| ) %>% | |
| select( | |
| 現場ID = site_id, | |
| 現場名 = site_name, | |
| 市区町村 = city, | |
| 必要人数 = required_guards, | |
| "対応可能人数(同一市内)" = available_guards, | |
| "不足人数" = shortage | |
| ) | |
| }) | |
| # 地図描画 | |
| output$map <- renderLeaflet({ | |
| dd <- daily_data() | |
| # ベースマップ(北海道あたりを初期表示) | |
| m <- leaflet() %>% | |
| addTiles() %>% | |
| setView(lng = 142.0, lat = 43.0, zoom = 6) | |
| # 工事現場 | |
| if (nrow(dd$active_sites) > 0) { | |
| m <- m %>% | |
| addCircleMarkers( | |
| data = dd$active_sites, | |
| ~lng, ~lat, | |
| radius = 8, | |
| color = "blue", | |
| fillOpacity = 0.8, | |
| label = ~paste0(site_name, "(必要人数: ", required_guards, "人)"), | |
| group = "sites" | |
| ) | |
| } | |
| # 警備員 | |
| if (input$show_guards && nrow(dd$available_guards) > 0) { | |
| m <- m %>% | |
| addCircleMarkers( | |
| data = dd$available_guards, | |
| ~lng, ~lat, | |
| radius = 5, | |
| color = "red", | |
| fillOpacity = 0.7, | |
| label = ~paste0(guard_name, "(", city, "在住)"), | |
| group = "guards" | |
| ) | |
| } | |
| m %>% addLayersControl( | |
| overlayGroups = c("sites", "guards"), | |
| options = layersControlOptions(collapsed = FALSE) | |
| ) | |
| }) | |
| # 集計テーブル | |
| output$summary <- renderTable({ | |
| summary_tbl() | |
| }) | |
| } | |
| shinyApp(ui, server) |