Ch 03. leaflet으로 지도 시각화
숫자만으로 설명하기 어려운 지역 데이터는 지도에 올리면 한눈에 보입니다. leaflet은 OpenStreetMap 기반의 인터랙티브 지도 라이브러리로, R의 leaflet 패키지를 통해 Shiny 앱에 드래그·줌·팝업을 갖춘 지도를 손쉽게 삽입할 수 있습니다.
leaflet 설치
install.packages("leaflet")
기본 지도 만들기
leaflet()으로 지도 객체를 만들고, 파이프(%>%)로 레이어를 쌓습니다.
library(shiny)
library(leaflet)
ui <- fluidPage(
titlePanel("기본 지도"),
leafletOutput("map", height = "500px") # leafletOutput으로 자리 확보
)
server <- function(input, output, session) {
output$map <- renderLeaflet({ # renderLeaflet으로 렌더링
leaflet() %>%
addTiles() %>% # OpenStreetMap 타일 추가
setView(
lng = 126.978, # 서울 경도
lat = 37.566, # 서울 위도
zoom = 11 # 줌 레벨 (1~18)
)
})
}
shinyApp(ui, server)
addTiles()는 기본 OpenStreetMap 타일을 불러옵니다. 다른 지도 스타일로 바꾸려면 addProviderTiles()를 씁니다.
leaflet() %>%
addProviderTiles(providers$CartoDB.Positron) # 밝은 톤의 심플 지도
# providers$CartoDB.DarkMatter # 다크 모드 지도
# providers$Esri.WorldImagery # 위성 사진
# providers$OpenTopoMap # 지형도
addMarkers: 마커 추가
library(shiny)
library(leaflet)
# 주요 도시 데이터
cities <- data.frame(
name = c("서울", "부산", "대구", "인천", "광주", "대전"),
lat = c(37.566, 35.179, 35.871, 37.456, 35.160, 36.351),
lng = c(126.978, 129.076, 128.601, 126.705, 126.851, 127.385),
pop = c(9736962, 3404423, 2418346, 2948375, 1441970, 1471040),
stringsAsFactors = FALSE
)
ui <- fluidPage(
leafletOutput("city_map", height = "500px")
)
server <- function(input, output, session) {
output$city_map <- renderLeaflet({
leaflet(cities) %>%
addTiles() %>%
setView(lng = 127.9, lat = 36.5, zoom = 7) %>%
addMarkers(
lng = ~lng,
lat = ~lat,
popup = ~paste0(
"<b>", name, "</b><br>",
"인구: ", format(pop, big.mark = ","), "명"
),
label = ~name # 마우스 호버 시 표시
)
})
}
shinyApp(ui, server)
popup은 클릭 시 나타나는 말풍선이고, label은 마우스를 올렸을 때 나타나는 툴팁입니다. 둘 다 HTML을 지원합니다.
addCircleMarkers: 원형 마커
값의 크기를 원의 반지름으로 표현할 때 씁니다. 인구, 매출, 사고 건수 같은 양적 변수를 시각적으로 비교할 수 있습니다.
server <- function(input, output, session) {
output$city_map <- renderLeaflet({
leaflet(cities) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
setView(lng = 127.9, lat = 36.5, zoom = 7) %>%
addCircleMarkers(
lng = ~lng,
lat = ~lat,
radius = ~sqrt(pop / 100000), # 인구에 비례한 반지름
color = "#2c3e50",
weight = 1,
fillColor = "#3498db",
fillOpacity = 0.7,
popup = ~paste0("<b>", name, "</b><br>인구: ",
format(pop, big.mark = ","), "명"),
label = ~name
)
})
}
색상 팔레트로 값 시각화
colorNumeric(), colorFactor(), colorBin(), colorQuantile()로 색상 팔레트를 만들어 마커나 폴리곤에 적용합니다.
library(shiny)
library(leaflet)
ui <- fluidPage(
selectInput("palette", "색상 팔레트",
choices = c("YlOrRd", "Blues", "Greens", "RdBu")),
leafletOutput("choropleth_map", height = "500px")
)
server <- function(input, output, session) {
output$choropleth_map <- renderLeaflet({
# 임의 데이터 생성 (실제로는 sf/sp 폴리곤 데이터 사용)
cities_with_value <- cities
cities_with_value$value <- cities_with_value$pop / 1e6
pal <- colorNumeric(
palette = input$palette,
domain = cities_with_value$value
)
leaflet(cities_with_value) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
setView(lng = 127.9, lat = 36.5, zoom = 7) %>%
addCircleMarkers(
lng = ~lng,
lat = ~lat,
radius = 15,
fillColor = ~pal(value),
fillOpacity = 0.8,
color = "white",
weight = 2,
popup = ~paste0(name, ": ", round(value, 2), "백만 명")
) %>%
addLegend(
position = "bottomright",
pal = pal,
values = ~value,
title = "인구(백만)",
opacity = 0.8
)
})
}
shinyApp(ui, server)
addLegend()로 색상 범례를 추가합니다. position은 "bottomright", "bottomleft", "topright", "topleft" 중 하나입니다.
leafletProxy: 지도를 다시 그리지 않고 업데이트
입력 값이 바뀔 때마다 renderLeaflet()이 실행되면 지도가 처음부터 다시 로드됩니다. leafletProxy()를 쓰면 마커만 교체하거나 뷰만 이동할 수 있어 훨씬 자연스럽습니다.
library(shiny)
library(leaflet)
cities <- data.frame(
name = c("서울", "부산", "대구", "인천"),
lat = c(37.566, 35.179, 35.871, 37.456),
lng = c(126.978, 129.076, 128.601, 126.705),
region = c("수도권", "영남", "영남", "수도권"),
stringsAsFactors = FALSE
)
ui <- fluidPage(
checkboxGroupInput("region_filter", "지역 필터",
choices = c("수도권", "영남"),
selected = c("수도권", "영남"),
inline = TRUE),
leafletOutput("map", height = "400px")
)
server <- function(input, output, session) {
# 초기 지도 한 번만 렌더링
output$map <- renderLeaflet({
leaflet() %>%
addTiles() %>%
setView(lng = 127.9, lat = 36.5, zoom = 7)
})
# 필터 변경 시 마커만 업데이트
observe({
filtered <- cities[cities$region %in% input$region_filter, ]
leafletProxy("map") %>%
clearMarkers() %>% # 기존 마커 삭제
addMarkers(
data = filtered,
lng = ~lng,
lat = ~lat,
label = ~name,
popup = ~paste0("<b>", name, "</b><br>지역: ", region)
)
})
}
shinyApp(ui, server)
leafletProxy("map")은 이미 렌더링된 지도에 접근합니다. clearMarkers(), clearShapes(), clearHeatmap() 등으로 기존 레이어를 지우고 새로 추가합니다.
주요 레이어 함수 정리
| 함수 | 용도 |
|---|---|
addTiles() |
OpenStreetMap 기본 타일 |
addProviderTiles() |
다양한 외부 지도 타일 |
addMarkers() |
핀 마커 |
addCircleMarkers() |
원형 마커 |
addCircles() |
반지름이 있는 원(미터 단위) |
addPolygons() |
폴리곤(행정 구역 등) |
addPolylines() |
선(도로, 경로 등) |
addHeatmap() |
히트맵 (leaflet.extras 패키지) |
addLegend() |
색상 범례 |
addMiniMap() |
미니 지도 (leaflet.extras 패키지) |