sugitora's picture
Update app.R
058b28d verified
# 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)